Compare commits
10 Commits
6514d5aa56
...
3a60ee1b28
Author | SHA1 | Date |
---|---|---|
xfnw | 3a60ee1b28 | |
xfnw | 9a8be92c1b | |
xfnw | 3fdd64772a | |
xfnw | 23eb92dae3 | |
xfnw | 02de2ce640 | |
xfnw | 4a99ba9b7c | |
xfnw | ecee487233 | |
xfnw | 666d3b8f69 | |
xfnw | ae5c9462b6 | |
xfnw | 44e559b8cc |
|
@ -64,9 +64,9 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
|||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "4.5.2"
|
||||
version = "4.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b230ab84b0ffdf890d5a10abdbc8b83ae1c4918275daea1ab8801f71536b2651"
|
||||
checksum = "949626d00e063efc93b6dca932419ceb5432f99769911c0b995f7e884c778813"
|
||||
dependencies = [
|
||||
"clap_builder",
|
||||
"clap_derive",
|
||||
|
@ -84,9 +84,9 @@ dependencies = [
|
|||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "4.5.0"
|
||||
version = "4.5.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "307bc0538d5f0f83b8248db3087aa92fe504e4691294d0c96c0eabc33f47ba47"
|
||||
checksum = "90239a040c80f5e14809ca132ddc4176ab33d5e17e49691793296e3fcb34d72f"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro2",
|
||||
|
@ -119,9 +119,9 @@ checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
|||
|
||||
[[package]]
|
||||
name = "heck"
|
||||
version = "0.4.1"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "95505c38b4572b2d910cecb0281560f54b440a19336cbbcb27bf6ce6adc6f5a8"
|
||||
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
|
@ -131,11 +131,10 @@ checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024"
|
|||
|
||||
[[package]]
|
||||
name = "irccrab"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
dependencies = [
|
||||
"clap",
|
||||
"rustls-pemfile",
|
||||
"rustls-pki-types",
|
||||
"tokio",
|
||||
"tokio-rustls",
|
||||
]
|
||||
|
@ -146,12 +145,6 @@ version = "0.2.153"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9c198f91728a82281a64e1f4f9eeb25d82cb32a5de251c6bd1b5154d63a8e7bd"
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.21"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c"
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.7.1"
|
||||
|
@ -205,9 +198,9 @@ checksum = "8afb450f006bf6385ca15ef45d71d2288452bc3683ce2e2cacc0d18e4be60b58"
|
|||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.78"
|
||||
version = "1.0.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2422ad645d89c99f8f3e6b88a9fdeca7fabeac836b1002371c4367c8f984aae"
|
||||
checksum = "e835ff2298f5721608eb1a980ecaee1aef2c132bf95ecc026a11b7bf3c01c02e"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
@ -248,7 +241,6 @@ version = "0.22.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e87c9956bd9807afa1f77e0f7594af32566e830e088a5576d27c5b6f30f49d41"
|
||||
dependencies = [
|
||||
"log",
|
||||
"ring",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
|
|
16
Cargo.toml
16
Cargo.toml
|
@ -1,15 +1,21 @@
|
|||
[package]
|
||||
name = "irccrab"
|
||||
description = "irc ping/ponger similar to ircdog"
|
||||
version = "0.1.0"
|
||||
version = "1.0.0"
|
||||
edition = "2021"
|
||||
license = "MIT"
|
||||
|
||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "4.5.2", default-features = false, features = ["derive", "std", "env", "help", "usage"] }
|
||||
clap = { version = "4.5.2", default-features = false, features = ["derive", "std", "help", "usage"] }
|
||||
rustls-pemfile = "2.1.1"
|
||||
rustls-pki-types = "1.3.1"
|
||||
tokio = { version = "1.36.0", features = ["rt-multi-thread", "tokio-macros", "macros", "net", "io-util", "io-std"] }
|
||||
tokio-rustls = "0.25.0"
|
||||
tokio = { version = "1.36.0", features = ["rt-multi-thread", "macros", "net", "io-util", "io-std"]}
|
||||
tokio-rustls = { version = "0.25.0", default-features = false, features = ["ring", "tls12"] }
|
||||
|
||||
[profile.smol]
|
||||
inherits = "release"
|
||||
opt-level = "z"
|
||||
panic = "abort"
|
||||
strip = true
|
||||
lto = true
|
||||
|
|
|
@ -0,0 +1,10 @@
|
|||
# irccrab 🦀
|
||||
|
||||
rust alternative to [ircdog](https://github.com/ergochat/ircdog)
|
||||
because i did not feel like installing go
|
||||
|
||||
- automatically responds to irc `PING`s
|
||||
- connects via tls (rustls) or plaintext
|
||||
- can use an alternative cafile or disable tls verification entirely
|
||||
- supports client certificate authentication
|
||||
|
|
@ -0,0 +1,94 @@
|
|||
{
|
||||
"nodes": {
|
||||
"flake-utils": {
|
||||
"inputs": {
|
||||
"systems": "systems"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1710146030,
|
||||
"narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "numtide",
|
||||
"repo": "flake-utils",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"naersk": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1698420672,
|
||||
"narHash": "sha256-/TdeHMPRjjdJub7p7+w55vyABrsJlt5QkznPYy55vKA=",
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"rev": "aeb58d5e8faead8980a807c840232697982d47b9",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-community",
|
||||
"repo": "naersk",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1712666087,
|
||||
"narHash": "sha256-WwjUkWsjlU8iUImbivlYxNyMB1L5YVqE8QotQdL9jWc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a76c4553d7e741e17f289224eda135423de0491d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"id": "nixpkgs",
|
||||
"type": "indirect"
|
||||
}
|
||||
},
|
||||
"nixpkgs_2": {
|
||||
"locked": {
|
||||
"lastModified": 1712666087,
|
||||
"narHash": "sha256-WwjUkWsjlU8iUImbivlYxNyMB1L5YVqE8QotQdL9jWc=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "a76c4553d7e741e17f289224eda135423de0491d",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"ref": "nixpkgs-unstable",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"flake-utils": "flake-utils",
|
||||
"naersk": "naersk",
|
||||
"nixpkgs": "nixpkgs_2"
|
||||
}
|
||||
},
|
||||
"systems": {
|
||||
"locked": {
|
||||
"lastModified": 1681028828,
|
||||
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "nix-systems",
|
||||
"repo": "default",
|
||||
"type": "github"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
{
|
||||
inputs = {
|
||||
flake-utils.url = "github:numtide/flake-utils";
|
||||
naersk.url = "github:nix-community/naersk";
|
||||
nixpkgs.url = "github:NixOS/nixpkgs/nixpkgs-unstable";
|
||||
};
|
||||
|
||||
outputs = { self, flake-utils, naersk, nixpkgs }:
|
||||
flake-utils.lib.eachDefaultSystem (system:
|
||||
let
|
||||
pkgs = (import nixpkgs) { inherit system; };
|
||||
naersk' = pkgs.callPackage naersk { };
|
||||
in rec {
|
||||
packages.irccrab = naersk'.buildPackage { src = ./.; };
|
||||
|
||||
defaultPackage = packages.irccrab;
|
||||
|
||||
devShell = pkgs.mkShell {
|
||||
nativeBuildInputs = with pkgs; [ rustc cargo clippy ];
|
||||
};
|
||||
});
|
||||
}
|
|
@ -0,0 +1,66 @@
|
|||
use std::sync::Arc;
|
||||
use tokio_rustls::rustls::{
|
||||
self,
|
||||
client::danger::HandshakeSignatureValid,
|
||||
crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider},
|
||||
pki_types::{CertificateDer, ServerName, UnixTime},
|
||||
DigitallySignedStruct,
|
||||
};
|
||||
|
||||
/// mostly borrowed from rustls/examples/src/bin/tlsclient-mio.rs,
|
||||
/// rustls seems quite insistent on making the process of disabling
|
||||
/// certificate verification as obnoxious as possible...
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PhonyVerify(CryptoProvider);
|
||||
|
||||
impl PhonyVerify {
|
||||
pub fn new(provider: CryptoProvider) -> Arc<Self> {
|
||||
Arc::new(Self(provider))
|
||||
}
|
||||
}
|
||||
|
||||
impl rustls::client::danger::ServerCertVerifier for PhonyVerify {
|
||||
fn verify_server_cert(
|
||||
&self,
|
||||
_end_entity: &CertificateDer<'_>,
|
||||
_intermediates: &[CertificateDer<'_>],
|
||||
_server_name: &ServerName<'_>,
|
||||
_ocsp: &[u8],
|
||||
_now: UnixTime,
|
||||
) -> Result<rustls::client::danger::ServerCertVerified, rustls::Error> {
|
||||
Ok(rustls::client::danger::ServerCertVerified::assertion())
|
||||
}
|
||||
|
||||
fn verify_tls12_signature(
|
||||
&self,
|
||||
message: &[u8],
|
||||
cert: &CertificateDer<'_>,
|
||||
dss: &DigitallySignedStruct,
|
||||
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||||
verify_tls12_signature(
|
||||
message,
|
||||
cert,
|
||||
dss,
|
||||
&self.0.signature_verification_algorithms,
|
||||
)
|
||||
}
|
||||
|
||||
fn verify_tls13_signature(
|
||||
&self,
|
||||
message: &[u8],
|
||||
cert: &CertificateDer<'_>,
|
||||
dss: &DigitallySignedStruct,
|
||||
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||||
verify_tls13_signature(
|
||||
message,
|
||||
cert,
|
||||
dss,
|
||||
&self.0.signature_verification_algorithms,
|
||||
)
|
||||
}
|
||||
|
||||
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
|
||||
self.0.signature_verification_algorithms.supported_schemes()
|
||||
}
|
||||
}
|
71
src/main.rs
71
src/main.rs
|
@ -1,11 +1,15 @@
|
|||
use clap::Parser;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::Arc;
|
||||
use std::{io::Write, path::PathBuf, sync::Arc};
|
||||
use tokio::{
|
||||
io::{self, AsyncBufReadExt, AsyncWriteExt, BufReader},
|
||||
net::TcpStream,
|
||||
};
|
||||
use tokio_rustls::{rustls, TlsConnector};
|
||||
use tokio_rustls::{
|
||||
rustls::{self, pki_types},
|
||||
TlsConnector,
|
||||
};
|
||||
|
||||
mod danger;
|
||||
|
||||
/// irc ping/ponger similar to ircdog
|
||||
#[derive(Debug, Parser)]
|
||||
|
@ -28,8 +32,8 @@ struct Opt {
|
|||
#[arg(required = true)]
|
||||
host: String,
|
||||
|
||||
#[arg(default_value = "6667")]
|
||||
port: u16,
|
||||
/// port defaults to 6667 or 6697 for tls
|
||||
port: Option<u16>,
|
||||
}
|
||||
|
||||
fn trim_mut(buf: &mut Vec<u8>) {
|
||||
|
@ -71,24 +75,34 @@ async fn send_pong(
|
|||
// https://github.com/tokio-rs/tokio/issues/3679
|
||||
//write.write_all_vectored(&[b"PONG ", pong].map(IoSlice::new)).await?;
|
||||
write.write_all(b"PONG ").await?;
|
||||
write.write_all(pong).await
|
||||
write.write_all(pong).await?;
|
||||
write.flush().await
|
||||
}
|
||||
|
||||
#[tokio::main]
|
||||
async fn main() {
|
||||
let opt = Opt::parse();
|
||||
|
||||
let stream = TcpStream::connect((opt.host.as_ref(), opt.port))
|
||||
let port = if let Some(port) = opt.port {
|
||||
port
|
||||
} else if opt.tls {
|
||||
6697
|
||||
} else {
|
||||
6667
|
||||
};
|
||||
|
||||
let stream = TcpStream::connect((opt.host.as_ref(), port))
|
||||
.await
|
||||
.expect("failed to connect");
|
||||
|
||||
if opt.tls {
|
||||
let config = rustls::ClientConfig::builder();
|
||||
let config = if opt.insecure {
|
||||
// rustls seems to make doing this intentionally annoying
|
||||
// https://docs.rs/rustls/latest/rustls/struct.ConfigBuilder.html
|
||||
// https://users.rust-lang.org/t/rustls-connecting-without-certificate-in-local-network/83822
|
||||
todo!();
|
||||
config
|
||||
.dangerous()
|
||||
.with_custom_certificate_verifier(danger::PhonyVerify::new(
|
||||
rustls::crypto::ring::default_provider(),
|
||||
))
|
||||
} else {
|
||||
let mut root_cert_store = rustls::RootCertStore::empty();
|
||||
let mut pem = std::io::BufReader::new(
|
||||
|
@ -99,17 +113,39 @@ async fn main() {
|
|||
}
|
||||
config.with_root_certificates(root_cert_store)
|
||||
};
|
||||
let config = if let Some(_cert) = opt.cert {
|
||||
// with_client_auth_cert
|
||||
todo!();
|
||||
let config = if let Some(cert) = opt.cert {
|
||||
use rustls_pemfile::Item;
|
||||
|
||||
let mut pem = std::io::BufReader::new(
|
||||
std::fs::File::open(cert).expect("cannot open client cert"),
|
||||
);
|
||||
let mut certs = Vec::new();
|
||||
let mut keys: Vec<pki_types::PrivateKeyDer> = Vec::new();
|
||||
|
||||
for c in rustls_pemfile::read_all(&mut pem) {
|
||||
match c.unwrap() {
|
||||
Item::X509Certificate(crt) => certs.push(crt),
|
||||
Item::Pkcs1Key(key) => keys.push(key.into()),
|
||||
Item::Pkcs8Key(key) => keys.push(key.into()),
|
||||
Item::Sec1Key(key) => keys.push(key.into()),
|
||||
e => eprintln!("unknown item in pem: {:?}", e),
|
||||
}
|
||||
}
|
||||
|
||||
config
|
||||
.with_client_auth_cert(certs, keys.pop().expect("no key found"))
|
||||
.expect("could not load client cert")
|
||||
} else {
|
||||
config.with_no_client_auth()
|
||||
};
|
||||
let connector = TlsConnector::from(Arc::new(config));
|
||||
let domain = rustls_pki_types::ServerName::try_from(opt.host)
|
||||
let domain = pki_types::ServerName::try_from(opt.host)
|
||||
.expect("invalid server name")
|
||||
.to_owned();
|
||||
let tlsstream = connector.connect(domain, stream).await.expect("failed to negotiate tls");
|
||||
let tlsstream = connector
|
||||
.connect(domain, stream)
|
||||
.await
|
||||
.expect("failed to negotiate tls");
|
||||
handle_irc(tlsstream).await;
|
||||
} else {
|
||||
handle_irc(stream).await;
|
||||
|
@ -134,6 +170,7 @@ async fn handle_irc(stream: impl io::AsyncReadExt + io::AsyncWriteExt) {
|
|||
stdbuf.push(b'\r');
|
||||
stdbuf.push(b'\n');
|
||||
write.write_all(&stdbuf).await.expect("cannot send");
|
||||
write.flush().await.expect("cannot send");
|
||||
|
||||
stdbuf.clear();
|
||||
}
|
||||
|
@ -148,7 +185,7 @@ async fn handle_irc(stream: impl io::AsyncReadExt + io::AsyncWriteExt) {
|
|||
|
||||
trim_mut(&mut ircbuf);
|
||||
ircbuf.push(b'\n');
|
||||
io::stdout().write_all(&ircbuf).await.expect("broken pipe");
|
||||
std::io::stdout().write_all(&ircbuf).expect("broken pipe");
|
||||
|
||||
ircbuf.clear();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue