Stat
This commit is contained in:
parent
e4fd3224cf
commit
fb408ab1ca
14
Cargo.toml
14
Cargo.toml
|
@ -10,12 +10,12 @@ repository = "https://forgejo.vanten-s.com/vanten-s/e2e-irc/"
|
||||||
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
argparse = "0.2.2"
|
async-trait = "0.1.80"
|
||||||
base64 = "0.21.4"
|
clap = { version = "4.5.8", features = ["derive"] }
|
||||||
dirs = "5.0.1"
|
|
||||||
eyre = "0.6.8"
|
eyre = "0.6.8"
|
||||||
ircparser-vanten = "0.2.1"
|
irc = { version = "1.0.0", default-features = false, features = ["tls-rust"] }
|
||||||
openssl = "0.10"
|
|
||||||
pgp = "0.10.2"
|
pgp = "0.10.2"
|
||||||
rand = "0.8.5"
|
rustls = "0.23.10"
|
||||||
toml = "0.8.2"
|
tokio = { version = "1.38.0", features = ["full"] }
|
||||||
|
tokio-rustls = "0.26.0"
|
||||||
|
webpki-roots = "0.26.3"
|
||||||
|
|
33
pk.asc
Normal file
33
pk.asc
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
-----BEGIN PGP PUBLIC KEY BLOCK-----
|
||||||
|
Version: Keybase OpenPGP v2.1.15
|
||||||
|
Comment: https://keybase.io/crypto
|
||||||
|
|
||||||
|
xo0EZok7pwEEAM+XGXrB8gHwe6QGNF+LgoEHlezGHqNwM6pvqOG7zp18u+uKLZ4p
|
||||||
|
X9Ahbny9+BZe+xQrvq7sHeu0zl3qigalEBhaiLbUqwJr+gTl8ubjUcy0uVYLMKEj
|
||||||
|
1KkS7aJW66S+YBgWeD95z+GJUOlieA/I+KKG6pmD4OIo4wOAi5D7hEOLABEBAAHN
|
||||||
|
AMK0BBMBCgAeBQJmiTunAhsvAwsJBwMVCggCHgECF4ADFgIBAhkBAAoJEJE12r5B
|
||||||
|
NfECNQoEAIKfmGUmvaH4mK7gOqUUKzPfNzHR8BRPveuKmmyZFJIjihS2MqFWMsp3
|
||||||
|
NDfWJ6P/MnFN2TW+0GYtAhLtT4mFVGm5MRJlRLStAWKAk5JB221JCWg5re/s7U4C
|
||||||
|
Ac6CFz/7eEM2x8LO/QkwAUi2YhaQNXxlA0OWqujw/wYUvqnO4abazo0EZok7pwEE
|
||||||
|
ANC6rVozjeDW0KT0odAukymQo3GGT/+TzrpZMfx6yHddZMoAxKZvbH7FwzuZI89i
|
||||||
|
+C3oKAajcAR0qxRuLhAvuZ+ULa1SSa0XRgAKpYl7O57B/etm1YLmgwJ0J/7xFkfW
|
||||||
|
kcj87TdWExT3XbzhWdbd4MuYTmYv1GLgWhuYHRj9RO3NABEBAAHCwIMEGAEKAA8F
|
||||||
|
AmaJO6cFCQ8JnAACGy4AqAkQkTXavkE18QKdIAQZAQoABgUCZok7pwAKCRCkd/34
|
||||||
|
XN68WmdcA/9Ub7IajkAfLhHUyKxljXRoB9wTHmQ+nwFr27Du5MNvzJz+TAa6hjcb
|
||||||
|
gT2kNFNIFuURAVEbqcSKusVlBYU6wmXT4Oo3MeoOYhLdp3LCrt5Ide0FlGvKP34j
|
||||||
|
knh7kWKLBHigwCAcFd277EQEY2k/CO5Lcf9d1fQNb9BDVorAnzJb3ueBBACNyBAt
|
||||||
|
wladh66RhwmlIA81tX4P1eceExEmLMiheN2L3AhEA2EyqpVdkbVbbW8N+uFU6Yx0
|
||||||
|
4Ucx0AqdhXAa4H2MSYBm0r2p4p3PVtIcMBhVxYt+jsS7geMQvDXPFUbYwWoSDut5
|
||||||
|
Eq/yeqHoF9vN5NgmcIubREeTUroNjdbWYnT39s6NBGaJO6cBBACzfeLxDV1wmJ89
|
||||||
|
yU/UWFGJeAqsCAW4tOtfA40h4Mli2GQu+VR3noBfe/szRjlGuVwjbyL2+VFqCcys
|
||||||
|
aPEpZ5MJWcdhrKuOIGGgGuJhe8MZfDXR/LIm+pOz6C577PQqYRnCKVlsdEd8gDij
|
||||||
|
gLrY5c/UmjRS2YYa4Znc5IPWSW5KVwARAQABwsCDBBgBCgAPBQJmiTunBQkDwmcA
|
||||||
|
AhsuAKgJEJE12r5BNfECnSAEGQEKAAYFAmaJO6cACgkQrPwDhFjiVufSKAP9HEHc
|
||||||
|
Y7gJ8u5H/su5enEeEIcr2K//SE2P8ihu56NZbXxbue8W1iEXqMu77bPr9tpJEHP6
|
||||||
|
jqFliSaPDq8l7cFJg3nTs1Xv65REhrIUXDKXlxstvf9DTQPg7L6KbiDZ3McXuURw
|
||||||
|
wLRZto++8Piz9rvfm5RAD5Zzq/iFF0yqUyxhp45miQQAkBHakmIIi4JjG/VPZvNr
|
||||||
|
TSdkojREOzUPRykUjkCbTGuJ1094u+cWD/SYlCtpbCj3fqkPqZW0LcnyHrVHOsTE
|
||||||
|
M5vQT1btE0ohtxhQD/R65HFs+e4NjHKE6HpOCuzsMfh+nsyClSIurnLf/8Xisy5u
|
||||||
|
dNWmdpgEjpQ+kRaMlRf+/ik=
|
||||||
|
=glLb
|
||||||
|
-----END PGP PUBLIC KEY BLOCK-----
|
18
shell.nix
Normal file
18
shell.nix
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
{ pkgs ? import <nixpkgs> {} }:
|
||||||
|
|
||||||
|
pkgs.mkShell rec {
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
rustup
|
||||||
|
openssl
|
||||||
|
];
|
||||||
|
RUSTC_VERSION = "stable";
|
||||||
|
LD_LIBRARY_PATH = with pkgs; lib.makeLibraryPath [
|
||||||
|
openssl
|
||||||
|
];
|
||||||
|
shellHook = ''
|
||||||
|
rustup default $RUSTC_VERSION
|
||||||
|
rustup component add rust-analyzer
|
||||||
|
export PATH=$PATH:''${CARGO_HOME:-~/.cargo}/bin
|
||||||
|
export PATH=$PATH:''${RUSTUP_HOME:-~/.rustup}/toolchains/nightly-x86_64-unknown-linux-gnu/bin/
|
||||||
|
'';
|
||||||
|
}
|
60
src/cli.rs
Normal file
60
src/cli.rs
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
use std::str::FromStr;
|
||||||
|
|
||||||
|
use clap::Parser;
|
||||||
|
|
||||||
|
#[derive(Parser, Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
#[command(version, about)]
|
||||||
|
pub(super) struct Cli {
|
||||||
|
pub server: String,
|
||||||
|
|
||||||
|
pub secret_key: String,
|
||||||
|
|
||||||
|
#[arg(long, short)]
|
||||||
|
pub password: Option<String>,
|
||||||
|
|
||||||
|
#[arg(short, long, default_value_t = false)]
|
||||||
|
pub tls: bool,
|
||||||
|
|
||||||
|
#[arg(short, long, default_value_t = EncryptionMethods::PGP)]
|
||||||
|
pub encryption_method: EncryptionMethods,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone)]
|
||||||
|
pub(crate) enum EncryptionMethods {
|
||||||
|
PGP,
|
||||||
|
None,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Hash, PartialEq, Eq, Clone, Copy)]
|
||||||
|
pub struct EncryptionMethodParseError;
|
||||||
|
|
||||||
|
impl std::fmt::Display for EncryptionMethods {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
let name = match self {
|
||||||
|
Self::PGP => "pgp",
|
||||||
|
Self::None => "none",
|
||||||
|
};
|
||||||
|
|
||||||
|
f.write_str(name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl FromStr for EncryptionMethods {
|
||||||
|
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||||
|
match s.to_lowercase().as_str() {
|
||||||
|
"pgp" => Ok(Self::PGP),
|
||||||
|
"none" => Ok(Self::None),
|
||||||
|
_ => Err(EncryptionMethodParseError),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
type Err = EncryptionMethodParseError;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::fmt::Display for EncryptionMethodParseError {
|
||||||
|
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||||
|
f.write_str("Parsing Error")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl std::error::Error for EncryptionMethodParseError {}
|
|
@ -1,104 +1,131 @@
|
||||||
use crate::helpers::bytes_to_privmsg_base64;
|
use std::{io::ErrorKind, net::SocketAddr, sync::Arc};
|
||||||
use crate::{encryption, helpers, State};
|
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use pgp::{Deserializable, SignedPublicKey};
|
use irc::proto::Message;
|
||||||
use std::collections::HashMap;
|
use rustls::pki_types::ServerName;
|
||||||
use std::sync::mpsc::{Receiver, Sender};
|
use tokio::{io::BufReader, net::TcpStream};
|
||||||
|
use tokio_rustls::TlsConnector;
|
||||||
|
|
||||||
#[derive(Debug)]
|
use crate::{
|
||||||
struct InvalidCommand;
|
cli::Cli,
|
||||||
|
encryption::Encryptor,
|
||||||
impl std::fmt::Display for InvalidCommand {
|
stream::{AsyncReader, AsyncWriter},
|
||||||
fn fmt(&self, _f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for InvalidCommand {}
|
|
||||||
|
|
||||||
fn parse_bouncer_command(message: String, state: &mut State) -> Result<()> {
|
|
||||||
macro_rules! unwrap_option {
|
|
||||||
($t:expr) => {
|
|
||||||
match $t {
|
|
||||||
Some(val) => val,
|
|
||||||
None => return Err(InvalidCommand.into()),
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
}
|
|
||||||
|
|
||||||
let mut splitted = message.split(' ');
|
pub async fn handle(
|
||||||
match unwrap_option!(splitted.next()) {
|
encryptor: Encryptor,
|
||||||
"ALLOW_UNENCRYPTED" => state
|
reader: Box<dyn AsyncReader>,
|
||||||
.nicks_without_encryption
|
writer: Box<dyn AsyncWriter>,
|
||||||
.push(unwrap_option!(splitted.next()).to_string().to_lowercase()),
|
addr: SocketAddr,
|
||||||
_ => return Err(InvalidCommand.into()),
|
config: Cli,
|
||||||
};
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_message_from_client(
|
|
||||||
recieved: &str,
|
|
||||||
public_key: &Vec<u8>,
|
|
||||||
server: &str,
|
|
||||||
keys: &mut HashMap<String, SignedPublicKey>,
|
|
||||||
writer_channel_tx: &Sender<String>,
|
|
||||||
writer_channel_rx: &Receiver<String>,
|
|
||||||
listener_channel_tx: &Sender<String>,
|
|
||||||
_listener_channel_rx: &Receiver<String>,
|
|
||||||
state: &mut State,
|
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let mut recieved = recieved.to_string();
|
let client_reader = reader;
|
||||||
|
let client_writer = writer;
|
||||||
|
|
||||||
if recieved.split(' ').count() == 1 {
|
let server_reader: Box<dyn AsyncReader>;
|
||||||
recieved += " ";
|
let server_writer: Box<dyn AsyncWriter>;
|
||||||
|
|
||||||
|
if config.tls {
|
||||||
|
let root_store =
|
||||||
|
rustls::RootCertStore::from_iter(webpki_roots::TLS_SERVER_ROOTS.iter().cloned());
|
||||||
|
|
||||||
|
let tls_config = rustls::ClientConfig::builder()
|
||||||
|
.with_root_certificates(root_store)
|
||||||
|
.with_no_client_auth();
|
||||||
|
|
||||||
|
let connector = TlsConnector::from(Arc::new(tls_config));
|
||||||
|
let dnsname = ServerName::try_from(config.server.clone())?;
|
||||||
|
|
||||||
|
let stream = TcpStream::connect((config.server.as_str(), 6697)).await?;
|
||||||
|
let stream = connector.connect(dnsname, stream).await?;
|
||||||
|
|
||||||
|
let server = stream;
|
||||||
|
|
||||||
|
let (reader, writer) = tokio::io::split(server);
|
||||||
|
|
||||||
|
server_reader = Box::new(BufReader::new(reader));
|
||||||
|
server_writer = Box::new(writer);
|
||||||
|
} else {
|
||||||
|
let stream = TcpStream::connect((config.server.as_str(), 6667)).await?;
|
||||||
|
|
||||||
|
let (reader, writer) = tokio::io::split(stream);
|
||||||
|
|
||||||
|
server_reader = Box::new(BufReader::new(reader));
|
||||||
|
server_writer = Box::new(writer);
|
||||||
}
|
}
|
||||||
|
|
||||||
let parsed = ircparser::parse(&recieved);
|
tokio::spawn(async move {
|
||||||
let command = match parsed {
|
let mut server = server_writer;
|
||||||
Ok(val) => val[0].clone(),
|
let mut client = client_reader;
|
||||||
Err(_) => {
|
loop {
|
||||||
writer_channel_tx.send(recieved)?;
|
let line = match client.read_line().await {
|
||||||
return Ok(());
|
Ok(v) => v,
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
ErrorKind::BrokenPipe => break,
|
||||||
|
_ => {
|
||||||
|
println!("Couldn't read client: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
let parsed: Message = match line.parse() {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => {
|
||||||
|
println!("Invalid command: {line}. Error: {e}");
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
if command.command == "PRIVMSG" && !command.params[0].starts_with('#') {
|
match parsed.command {
|
||||||
if command.params[0] == "BOUNCER" {
|
irc::proto::Command::PRIVMSG(target, string) => {
|
||||||
return parse_bouncer_command(command.params[1].clone(), state);
|
if !target.starts_with('#') {
|
||||||
|
if !§
|
||||||
|
println!("User wants to send a message to {target}. Message: {string}");
|
||||||
}
|
}
|
||||||
if state
|
|
||||||
.nicks_without_encryption
|
|
||||||
.contains(&command.params[0].to_lowercase())
|
|
||||||
{
|
|
||||||
writer_channel_tx.send(recieved)?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
}
|
||||||
let other = &command.params[0];
|
_ => {}
|
||||||
|
};
|
||||||
|
|
||||||
if !keys.contains_key(other) {
|
match server.write_string(line).await {
|
||||||
helpers::send_key(writer_channel_tx, other, public_key)?;
|
Ok(_) => {}
|
||||||
let key = helpers::recieve_message_base64(
|
Err(e) => match e.kind() {
|
||||||
writer_channel_rx,
|
ErrorKind::BrokenPipe => break,
|
||||||
listener_channel_tx,
|
_ => {
|
||||||
"127.0.0.1",
|
println!("Couldn't write server: {e}");
|
||||||
server,
|
continue;
|
||||||
other,
|
|
||||||
"END_KEY",
|
|
||||||
)?;
|
|
||||||
let key = SignedPublicKey::from_bytes(key.as_slice())?;
|
|
||||||
keys.insert(other.to_string(), key);
|
|
||||||
}
|
}
|
||||||
|
},
|
||||||
let foreign_key = keys.get(other).unwrap();
|
};
|
||||||
|
|
||||||
writer_channel_tx.send(format!("PRIVMSG {other} START_MESSAGE\r\n"))?;
|
|
||||||
writer_channel_tx.send(bytes_to_privmsg_base64(
|
|
||||||
&encryption::encrypt(foreign_key, &command.params[1])?,
|
|
||||||
other,
|
|
||||||
))?;
|
|
||||||
writer_channel_tx.send(format!("PRIVMSG {other} END_MESSAGE\r\n"))?;
|
|
||||||
} else {
|
|
||||||
writer_channel_tx.send(recieved.replace("127.0.0.1", server))?;
|
|
||||||
}
|
}
|
||||||
|
println!("Client at {addr} disconnected");
|
||||||
|
});
|
||||||
|
|
||||||
|
tokio::spawn(async move {
|
||||||
|
let mut server = server_reader;
|
||||||
|
let mut client = client_writer;
|
||||||
|
loop {
|
||||||
|
let line = match server.read_line().await {
|
||||||
|
Ok(v) => v,
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
ErrorKind::BrokenPipe => break,
|
||||||
|
_ => {
|
||||||
|
println!("Couldn't read server: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
match client.write_string(line).await {
|
||||||
|
Ok(_) => {}
|
||||||
|
Err(e) => match e.kind() {
|
||||||
|
ErrorKind::BrokenPipe => break,
|
||||||
|
_ => {
|
||||||
|
println!("Couldn't write client: {e}");
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,29 +0,0 @@
|
||||||
use eyre::Result;
|
|
||||||
use pgp::crypto::sym::SymmetricKeyAlgorithm;
|
|
||||||
use pgp::ser::Serialize;
|
|
||||||
use pgp::{Deserializable, Message, SignedPublicKey, SignedSecretKey};
|
|
||||||
use rand::prelude::*;
|
|
||||||
|
|
||||||
pub fn encrypt(key: &SignedPublicKey, message: &str) -> Result<Vec<u8>, pgp::errors::Error> {
|
|
||||||
let message = Message::new_literal("none", message);
|
|
||||||
let mut rng = StdRng::from_entropy();
|
|
||||||
|
|
||||||
let message = message.encrypt_to_keys(&mut rng, SymmetricKeyAlgorithm::AES128, &[key])?;
|
|
||||||
|
|
||||||
message.to_bytes()
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn decrypt<'a>(
|
|
||||||
key: &'a SignedSecretKey,
|
|
||||||
message: &[u8],
|
|
||||||
password: &'a str,
|
|
||||||
) -> Result<String, pgp::errors::Error> {
|
|
||||||
let message = Message::from_bytes(message)?; // Convert message bytes to message object
|
|
||||||
let message = message.decrypt(|| password.to_string(), &[key])?.0; // Decrypt
|
|
||||||
let message = message.map(|x| x.unwrap()).collect::<Vec<Message>>(); // Get all messages
|
|
||||||
let message = &message[0]; // Get first message
|
|
||||||
let message = message.get_content()?.unwrap_or(Vec::new()); // Get message content as Vec<u8>
|
|
||||||
let message = String::from_utf8(message).unwrap(); // Convert to String
|
|
||||||
|
|
||||||
Ok(message)
|
|
||||||
}
|
|
65
src/encryption/mod.rs
Normal file
65
src/encryption/mod.rs
Normal file
|
@ -0,0 +1,65 @@
|
||||||
|
use eyre::Result;
|
||||||
|
|
||||||
|
use crate::cli::EncryptionMethods;
|
||||||
|
use none::NoneEncryptor;
|
||||||
|
use pgp::PGPEncryptor;
|
||||||
|
|
||||||
|
pub mod none;
|
||||||
|
pub mod pgp;
|
||||||
|
|
||||||
|
pub trait EncryptionMethod: std::fmt::Debug + Send {
|
||||||
|
fn initialise(key: String, password: Option<String>) -> Result<Self>
|
||||||
|
where
|
||||||
|
Self: Sized;
|
||||||
|
|
||||||
|
fn encrypt(&self, message: String, name: String) -> Result<String>;
|
||||||
|
fn decrypt(&self, message: String, name: String) -> Result<String>;
|
||||||
|
|
||||||
|
fn register_key(&mut self, name: String, key: String) -> Result<()>;
|
||||||
|
|
||||||
|
fn public_key(&self) -> String;
|
||||||
|
|
||||||
|
fn clone(&self) -> Box<dyn EncryptionMethod>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Encryptor {
|
||||||
|
encryption_method: Box<dyn EncryptionMethod>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Encryptor {
|
||||||
|
pub fn initialise(
|
||||||
|
encryption_method: EncryptionMethods,
|
||||||
|
key: String,
|
||||||
|
password: Option<String>,
|
||||||
|
) -> Result<Self> {
|
||||||
|
use EncryptionMethods as EM;
|
||||||
|
|
||||||
|
let encryption_method: Box<dyn EncryptionMethod> = match encryption_method {
|
||||||
|
EM::PGP => Box::new(PGPEncryptor::initialise(key, password)?),
|
||||||
|
EM::None => Box::new(NoneEncryptor::initialise(key, password)?),
|
||||||
|
};
|
||||||
|
|
||||||
|
Ok(Encryptor { encryption_method })
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn register_key(&mut self, name: String, key: String) -> Result<()> {
|
||||||
|
self.encryption_method.register_key(name, key)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn encrypt(&self, message: String, name: String) -> Result<String> {
|
||||||
|
self.encryption_method.encrypt(message, name)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn decrypt(&self, message: String, name: String) -> Result<String> {
|
||||||
|
self.encryption_method.decrypt(message, name)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Clone for Encryptor {
|
||||||
|
fn clone(&self) -> Self {
|
||||||
|
Encryptor {
|
||||||
|
encryption_method: self.encryption_method.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
32
src/encryption/none.rs
Normal file
32
src/encryption/none.rs
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
use eyre::Result;
|
||||||
|
|
||||||
|
use super::EncryptionMethod;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct NoneEncryptor;
|
||||||
|
|
||||||
|
impl EncryptionMethod for NoneEncryptor {
|
||||||
|
fn encrypt(&self, message: String, _name: String) -> eyre::Result<String> {
|
||||||
|
Ok(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt(&self, message: String, _name: String) -> eyre::Result<String> {
|
||||||
|
Ok(message)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public_key(&self) -> String {
|
||||||
|
String::new()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialise(_key: String, _password: Option<String>) -> eyre::Result<Self> {
|
||||||
|
Ok(Self)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_key(&mut self, _name: String, _key: String) -> Result<()> {
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone(&self) -> Box<dyn EncryptionMethod> {
|
||||||
|
Box::new(Clone::clone(self))
|
||||||
|
}
|
||||||
|
}
|
76
src/encryption/pgp.rs
Normal file
76
src/encryption/pgp.rs
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
use std::{
|
||||||
|
collections::HashMap,
|
||||||
|
fs,
|
||||||
|
sync::{Arc, Mutex},
|
||||||
|
};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use eyre::Result;
|
||||||
|
use irc::proto::{Command, Message};
|
||||||
|
use pgp::{types::SecretKeyTrait, Deserializable};
|
||||||
|
use tokio::sync::mpsc;
|
||||||
|
|
||||||
|
use crate::stream::{AsyncReader, AsyncWriter};
|
||||||
|
|
||||||
|
use super::EncryptionMethod;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct PGPEncryptor {
|
||||||
|
secret_key: pgp::SignedSecretKey,
|
||||||
|
public_key: String,
|
||||||
|
password: String,
|
||||||
|
keychain: Arc<Mutex<HashMap<String, pgp::SignedPublicKey>>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl EncryptionMethod for PGPEncryptor {
|
||||||
|
fn encrypt(&self, _message: String, _name: String) -> Result<String> {
|
||||||
|
let _ = self.secret_key.clone();
|
||||||
|
let _ = self.password.clone();
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn decrypt(&self, _message: String, _name: String) -> Result<String> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn initialise(key: String, password: Option<String>) -> Result<Self> {
|
||||||
|
let password = password.unwrap_or("".to_string());
|
||||||
|
|
||||||
|
let secret_key = fs::read_to_string(&key)?;
|
||||||
|
let secret_key = pgp::SignedSecretKey::from_string(&secret_key)?.0;
|
||||||
|
let public_key = secret_key
|
||||||
|
.public_key()
|
||||||
|
.sign(&secret_key, || password.clone())?
|
||||||
|
.to_armored_string(None)?;
|
||||||
|
let keychain = Arc::new(Mutex::new(HashMap::new()));
|
||||||
|
|
||||||
|
Ok(Self {
|
||||||
|
secret_key,
|
||||||
|
public_key,
|
||||||
|
password,
|
||||||
|
keychain,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
fn public_key(&self) -> String {
|
||||||
|
self.public_key.clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn register_key(&mut self, name: String, key: String) -> Result<()> {
|
||||||
|
dbg!("Registering Key");
|
||||||
|
dbg!("Key: {key}");
|
||||||
|
let key = pgp::SignedPublicKey::from_string(&key)?.0;
|
||||||
|
dbg!("Parsed PK");
|
||||||
|
|
||||||
|
let mut keychain = self.keychain.lock().unwrap();
|
||||||
|
dbg!("Got lock");
|
||||||
|
keychain.insert(name, key);
|
||||||
|
dbg!("Inserted");
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn clone(&self) -> Box<dyn EncryptionMethod> {
|
||||||
|
Box::new(Clone::clone(self))
|
||||||
|
}
|
||||||
|
}
|
144
src/helpers.rs
144
src/helpers.rs
|
@ -1,144 +0,0 @@
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
|
||||||
use eyre::Result;
|
|
||||||
use std::sync::mpsc::{self, Receiver, Sender};
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct IrcParseError;
|
|
||||||
|
|
||||||
impl std::fmt::Display for IrcParseError {
|
|
||||||
fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl std::error::Error for IrcParseError {}
|
|
||||||
|
|
||||||
static MAX_LENGTH: usize = 300;
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! unwrap_or_return_result {
|
|
||||||
($e:expr) => {
|
|
||||||
match $e {
|
|
||||||
Ok(val) => val,
|
|
||||||
Err(_) => return Ok(()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
#[macro_export]
|
|
||||||
macro_rules! unwrap_or_return_option {
|
|
||||||
($e:expr) => {
|
|
||||||
match $e {
|
|
||||||
Some(val) => val,
|
|
||||||
None => return Ok(()),
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct State {
|
|
||||||
pub nicks_without_encryption: Vec<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl State {
|
|
||||||
pub fn new() -> Self {
|
|
||||||
State {
|
|
||||||
nicks_without_encryption: vec![
|
|
||||||
"nickserv".to_string(),
|
|
||||||
"chanserv".to_string(),
|
|
||||||
"hostserv".to_string(),
|
|
||||||
],
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn forward(
|
|
||||||
message: String,
|
|
||||||
stream: &Sender<String>,
|
|
||||||
server_local: &str,
|
|
||||||
server_forward: &str,
|
|
||||||
) -> Result<(), mpsc::SendError<String>> {
|
|
||||||
match ircparser::parse(&message) {
|
|
||||||
Ok(val) => match val[0].command.as_str() {
|
|
||||||
"PRIVMSG" => stream.send(message),
|
|
||||||
_ => stream.send(message.replace(server_local, server_forward)),
|
|
||||||
},
|
|
||||||
Err(_) => stream.send(message.replace(server_local, server_forward)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn bytes_to_privmsg_base64(message: &Vec<u8>, reciever: &str) -> String {
|
|
||||||
let message_length = MAX_LENGTH - format!("PRIVMSG {reciever} :\r\n").len();
|
|
||||||
let encoded = general_purpose::STANDARD.encode(message);
|
|
||||||
|
|
||||||
let mut command = String::new();
|
|
||||||
for line in encoded
|
|
||||||
.chars()
|
|
||||||
.collect::<Vec<char>>()
|
|
||||||
.chunks(message_length)
|
|
||||||
.map(|c| c.iter().collect::<String>())
|
|
||||||
{
|
|
||||||
dbg!(&line);
|
|
||||||
command.push_str(&format!("PRIVMSG {reciever} :{line}\r\n"));
|
|
||||||
}
|
|
||||||
|
|
||||||
println!("{}", encoded);
|
|
||||||
println!("{}", command);
|
|
||||||
command
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn send_key(sender: &Sender<String>, reciever: &str, key: &Vec<u8>) -> Result<()> {
|
|
||||||
sender.send(format!("PRIVMSG {reciever} :START_KEY\r\n"))?;
|
|
||||||
sender.send(bytes_to_privmsg_base64(key, reciever))?;
|
|
||||||
sender.send(format!("PRIVMSG {reciever} :END_KEY\r\n"))?;
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_nick(userstring: &str) -> Option<String> {
|
|
||||||
let userstring = userstring.chars().collect::<Vec<char>>();
|
|
||||||
let start_pos = userstring.iter().position(|&x| x == ':')? + 1;
|
|
||||||
let end_pos = userstring.iter().position(|&x| x == '!')?;
|
|
||||||
Some(userstring[start_pos..end_pos].iter().collect::<String>())
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn recieve_message_base64(
|
|
||||||
writer_channel_rx: &Receiver<String>,
|
|
||||||
forward_stream: &Sender<String>,
|
|
||||||
server_local: &str,
|
|
||||||
server_forward: &str,
|
|
||||||
sender: &str,
|
|
||||||
end: &str,
|
|
||||||
) -> Result<Vec<u8>> {
|
|
||||||
let mut message: Vec<String> = Vec::new();
|
|
||||||
|
|
||||||
while !message.contains(&end.to_string()) {
|
|
||||||
let recieved_raw = writer_channel_rx.recv()?;
|
|
||||||
|
|
||||||
let parse_result = ircparser::parse(&recieved_raw);
|
|
||||||
|
|
||||||
let recieved = match parse_result {
|
|
||||||
Ok(mut val) => val.pop_back().unwrap(),
|
|
||||||
Err(_) => return Err(IrcParseError.into()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let begin_source_reciever = format!(":{sender}!");
|
|
||||||
if recieved.command != "PRIVMSG"
|
|
||||||
|| !recieved
|
|
||||||
.source
|
|
||||||
.clone()
|
|
||||||
.unwrap_or("".to_string())
|
|
||||||
.starts_with(&begin_source_reciever)
|
|
||||||
|| recieved.params[0].starts_with('#')
|
|
||||||
{
|
|
||||||
forward(recieved_raw, forward_stream, server_local, server_forward)?;
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
message.push(recieved.params[1].clone());
|
|
||||||
}
|
|
||||||
message.pop();
|
|
||||||
|
|
||||||
let foreign_key = dbg!(message.concat());
|
|
||||||
let foreign_key = general_purpose::STANDARD.decode(foreign_key)?;
|
|
||||||
Ok(foreign_key)
|
|
||||||
}
|
|
|
@ -1,59 +0,0 @@
|
||||||
use std::io::{ErrorKind, Read, Write};
|
|
||||||
use std::net::{TcpListener, TcpStream};
|
|
||||||
use std::sync::mpsc::{self, TryRecvError};
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
fn stream_handler(tx: &mpsc::Sender<String>, rx: &mpsc::Receiver<String>, mut stream: TcpStream) {
|
|
||||||
loop {
|
|
||||||
let mut buffer: Vec<u8> = Vec::new();
|
|
||||||
let mut buf: [u8; 1] = [0];
|
|
||||||
let newline: u8 = b'\n';
|
|
||||||
|
|
||||||
while buf[0] != newline {
|
|
||||||
match stream.read(&mut buf) {
|
|
||||||
Ok(_length) => buffer.push(buf[0]),
|
|
||||||
Err(_error) => match _error.kind() {
|
|
||||||
ErrorKind::WouldBlock => {}
|
|
||||||
_ => {
|
|
||||||
dbg!(_error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
match rx.try_recv() {
|
|
||||||
Ok(value) => {
|
|
||||||
match stream.write_all(value.as_bytes()) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(_e) => {
|
|
||||||
dbg!(_e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
Err(TryRecvError::Empty) => {}
|
|
||||||
Err(TryRecvError::Disconnected) => return,
|
|
||||||
}
|
|
||||||
thread::sleep(Duration::from_micros(100));
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = tx.send(dbg!(String::from_utf8_lossy(&buffer).to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn listen_to_client(tx: mpsc::Sender<String>, rx: mpsc::Receiver<String>, port: String) {
|
|
||||||
let listener = TcpListener::bind("127.0.0.1:".to_string() + &port)
|
|
||||||
.expect(&("Couldn't start listener on 127.0.0.1 port ".to_string() + &port));
|
|
||||||
loop {
|
|
||||||
let (stream, ip) = listener.accept().unwrap();
|
|
||||||
println!("Got connection from {ip}");
|
|
||||||
|
|
||||||
stream
|
|
||||||
.set_nonblocking(true)
|
|
||||||
.expect("Couldn't set nonblocking");
|
|
||||||
|
|
||||||
stream_handler(&tx, &rx, stream);
|
|
||||||
println!("Closed connection with {ip}");
|
|
||||||
let _ = tx.send("DUMMY CLOSE_CONNECTION".to_string());
|
|
||||||
}
|
|
||||||
}
|
|
160
src/main.rs
160
src/main.rs
|
@ -1,150 +1,36 @@
|
||||||
use argparse::{ArgumentParser, Store};
|
use clap::Parser;
|
||||||
use dirs::config_local_dir;
|
use encryption::Encryptor;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use helpers::State;
|
use tokio::io::BufReader;
|
||||||
use pgp::{Deserializable, SignedPublicKey, SignedSecretKey};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::fs;
|
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
use toml::Table;
|
|
||||||
|
|
||||||
|
mod cli;
|
||||||
mod client_handler;
|
mod client_handler;
|
||||||
mod encryption;
|
mod encryption;
|
||||||
mod helpers;
|
mod stream;
|
||||||
mod listener_server;
|
|
||||||
mod server_handler;
|
|
||||||
mod writer_client;
|
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
#[tokio::main]
|
||||||
let config_file = config_local_dir()
|
async fn main() -> Result<()> {
|
||||||
.expect("Couldn't get config directory")
|
let config = cli::Cli::parse();
|
||||||
.join("e2e-irc/config.toml");
|
|
||||||
|
|
||||||
if !config_file.exists() {
|
let listener = tokio::net::TcpListener::bind("127.0.0.1:6666").await?;
|
||||||
panic!("Create a config file at {}", config_file.display());
|
|
||||||
}
|
|
||||||
|
|
||||||
let parsed_config = String::from_utf8_lossy(&fs::read(config_file)?).parse::<Table>()?;
|
let encryptor = Encryptor::initialise(
|
||||||
|
config.encryption_method.clone(),
|
||||||
let public_key_location = parsed_config
|
config.secret_key.clone(),
|
||||||
.get("public_key")
|
config.password.clone(),
|
||||||
.expect("Coudln't get public_key. Try creating it in the config")
|
)?;
|
||||||
.as_str()
|
|
||||||
.expect("Couldn't convert public_key to str");
|
|
||||||
let secret_key_location = parsed_config
|
|
||||||
.get("secret_key")
|
|
||||||
.expect("Coudln't get secret_key. Try creating it in the config")
|
|
||||||
.as_str()
|
|
||||||
.expect("Couldn't convert secret_key to str");
|
|
||||||
|
|
||||||
let default_password = toml::Value::String(String::new());
|
|
||||||
|
|
||||||
let passwd = parsed_config
|
|
||||||
.get("passwd")
|
|
||||||
.unwrap_or(&default_password)
|
|
||||||
.as_str()
|
|
||||||
.expect("Coudln't convert passwd to str");
|
|
||||||
|
|
||||||
let mut server = "irc.vanten-s.com".to_string();
|
|
||||||
let mut port = "6666".to_string();
|
|
||||||
let mut server_port = "6697".to_string();
|
|
||||||
|
|
||||||
{
|
|
||||||
let mut ap = ArgumentParser::new();
|
|
||||||
ap.set_description("Encrypted IRC Bouncer");
|
|
||||||
ap.refer(&mut server)
|
|
||||||
.add_argument(
|
|
||||||
"server",
|
|
||||||
Store,
|
|
||||||
"The Address Of The Server The Bouncer Connects To",
|
|
||||||
)
|
|
||||||
.required();
|
|
||||||
ap.refer(&mut port)
|
|
||||||
.add_option(&["-p", "--port"], Store, "The Port The Bouncer Binds To");
|
|
||||||
ap.refer(&mut server_port).add_option(
|
|
||||||
&["--sp", "--server-port"],
|
|
||||||
Store,
|
|
||||||
"The TLS Enabled Port Of The Server",
|
|
||||||
);
|
|
||||||
ap.parse_args_or_exit();
|
|
||||||
}
|
|
||||||
|
|
||||||
let public_key = fs::read(public_key_location)?;
|
|
||||||
let secret_key = SignedSecretKey::from_bytes(fs::read(secret_key_location)?.as_slice())?;
|
|
||||||
|
|
||||||
let (listener_channel_send_tx, listener_channel_rx) = mpsc::channel();
|
|
||||||
let (listener_channel_tx, listener_channel_recv_rx) = mpsc::channel();
|
|
||||||
|
|
||||||
let (writer_channel_tx, writer_channel_send_rx) = mpsc::channel();
|
|
||||||
let (writer_channel_recv_tx, writer_channel_rx) = mpsc::channel();
|
|
||||||
|
|
||||||
let tmp_port = port.clone();
|
|
||||||
thread::spawn(move || {
|
|
||||||
listener_server::listen_to_client(
|
|
||||||
listener_channel_send_tx,
|
|
||||||
listener_channel_recv_rx,
|
|
||||||
tmp_port,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let tmp_port = server_port.clone();
|
|
||||||
let tmp_server = server.clone();
|
|
||||||
thread::spawn(move || {
|
|
||||||
writer_client::write_to_server(
|
|
||||||
&tmp_server,
|
|
||||||
&tmp_port,
|
|
||||||
writer_channel_send_rx,
|
|
||||||
writer_channel_recv_tx,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
|
|
||||||
let mut keys: HashMap<String, SignedPublicKey> = HashMap::new();
|
|
||||||
let mut state = State::new();
|
|
||||||
|
|
||||||
loop {
|
loop {
|
||||||
match listener_channel_rx.try_recv() {
|
let (socket, address) = listener.accept().await?;
|
||||||
Ok(message) => {
|
|
||||||
let _ = client_handler::handle_message_from_client(
|
|
||||||
&message,
|
|
||||||
&public_key,
|
|
||||||
&server,
|
|
||||||
&mut keys,
|
|
||||||
&writer_channel_tx,
|
|
||||||
&writer_channel_rx,
|
|
||||||
&listener_channel_tx,
|
|
||||||
&listener_channel_rx,
|
|
||||||
&mut state,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(error) => match error {
|
|
||||||
mpsc::TryRecvError::Empty => {}
|
|
||||||
mpsc::TryRecvError::Disconnected => panic!("listener_channel_rx disconnected"),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
match writer_channel_rx.try_recv() {
|
let (reader, writer) = tokio::io::split(socket);
|
||||||
Ok(message) => {
|
|
||||||
let _ = server_handler::handle_message_from_server(
|
|
||||||
&message,
|
|
||||||
&public_key,
|
|
||||||
&secret_key,
|
|
||||||
&server,
|
|
||||||
passwd,
|
|
||||||
&mut keys,
|
|
||||||
&writer_channel_tx,
|
|
||||||
&writer_channel_rx,
|
|
||||||
&listener_channel_tx,
|
|
||||||
&listener_channel_rx,
|
|
||||||
&state,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
Err(error) => match error {
|
|
||||||
mpsc::TryRecvError::Empty => {}
|
|
||||||
mpsc::TryRecvError::Disconnected => panic!("writer_channel_rx disconnected"),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
thread::sleep(Duration::from_millis(1));
|
tokio::spawn(client_handler::handle(
|
||||||
|
encryptor.clone(),
|
||||||
|
Box::new(BufReader::new(reader)),
|
||||||
|
Box::new(writer),
|
||||||
|
address,
|
||||||
|
config.clone(),
|
||||||
|
));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,87 +0,0 @@
|
||||||
use crate::unwrap_or_return_option;
|
|
||||||
use crate::unwrap_or_return_result;
|
|
||||||
use crate::State;
|
|
||||||
use crate::{encryption, helpers};
|
|
||||||
use eyre::Result;
|
|
||||||
use pgp::{Deserializable, SignedPublicKey, SignedSecretKey};
|
|
||||||
use std::collections::HashMap;
|
|
||||||
use std::sync::mpsc::{self, Receiver, Sender};
|
|
||||||
|
|
||||||
fn forward(
|
|
||||||
message: &str,
|
|
||||||
listener_channel_tx: &Sender<String>,
|
|
||||||
server: &str,
|
|
||||||
) -> Result<(), mpsc::SendError<String>> {
|
|
||||||
listener_channel_tx.send(message.replace(server, "127.0.0.1"))
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn handle_message_from_server(
|
|
||||||
recieved: &str,
|
|
||||||
public_key: &Vec<u8>,
|
|
||||||
secret_key: &SignedSecretKey,
|
|
||||||
server: &str,
|
|
||||||
passwd: &str,
|
|
||||||
keys: &mut HashMap<String, SignedPublicKey>,
|
|
||||||
writer_channel_tx: &Sender<String>,
|
|
||||||
writer_channel_rx: &Receiver<String>,
|
|
||||||
listener_channel_tx: &Sender<String>,
|
|
||||||
_listener_channel_rx: &Receiver<String>,
|
|
||||||
state: &State,
|
|
||||||
) -> Result<()> {
|
|
||||||
let recieved_parsed = &unwrap_or_return_result!(ircparser::parse(recieved))[0];
|
|
||||||
|
|
||||||
let default_reciever = String::new();
|
|
||||||
|
|
||||||
let reciever = match recieved_parsed.params.get(0) {
|
|
||||||
Some(val) => val,
|
|
||||||
None => &default_reciever,
|
|
||||||
};
|
|
||||||
|
|
||||||
if recieved_parsed.command != "PRIVMSG"
|
|
||||||
|| reciever.starts_with('#')
|
|
||||||
|| state.nicks_without_encryption.contains(reciever)
|
|
||||||
{
|
|
||||||
forward(recieved, listener_channel_tx, server)?;
|
|
||||||
return Ok(());
|
|
||||||
}
|
|
||||||
|
|
||||||
dbg!(&recieved_parsed);
|
|
||||||
|
|
||||||
let source = unwrap_or_return_option!(&recieved_parsed.source);
|
|
||||||
|
|
||||||
if recieved_parsed.params[1] == "START_MESSAGE" {
|
|
||||||
let sender = unwrap_or_return_option!(helpers::get_nick(source));
|
|
||||||
|
|
||||||
let message = helpers::recieve_message_base64(
|
|
||||||
writer_channel_rx,
|
|
||||||
listener_channel_tx,
|
|
||||||
server,
|
|
||||||
"127.0.0.1",
|
|
||||||
&sender,
|
|
||||||
"END_MESSAGE",
|
|
||||||
)?; // Get
|
|
||||||
let message = encryption::decrypt(secret_key, &message, passwd)?; // Decrypt
|
|
||||||
|
|
||||||
listener_channel_tx.send(recieved.replace("START_MESSAGE", &message))?; // Send
|
|
||||||
} else if recieved_parsed.params[1] == "START_KEY" {
|
|
||||||
let sender = unwrap_or_return_option!(helpers::get_nick(source));
|
|
||||||
let to_send = helpers::bytes_to_privmsg_base64(public_key, &sender);
|
|
||||||
writer_channel_tx.send(to_send)?;
|
|
||||||
writer_channel_tx.send(format!("PRIVMSG {sender} END_KEY\r\n"))?;
|
|
||||||
|
|
||||||
let foreign_key = helpers::recieve_message_base64(
|
|
||||||
writer_channel_rx,
|
|
||||||
listener_channel_tx,
|
|
||||||
server,
|
|
||||||
"127.0.0.1",
|
|
||||||
&sender,
|
|
||||||
"END_KEY",
|
|
||||||
)?;
|
|
||||||
dbg!(&foreign_key);
|
|
||||||
let key = SignedPublicKey::from_bytes(foreign_key.as_slice())?;
|
|
||||||
println!("Got a key from {sender}");
|
|
||||||
keys.insert(sender.to_string(), key);
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(())
|
|
||||||
}
|
|
40
src/stream.rs
Normal file
40
src/stream.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use std::io::{Error, ErrorKind};
|
||||||
|
|
||||||
|
use async_trait::async_trait;
|
||||||
|
use eyre::Result;
|
||||||
|
use tokio::io::{AsyncBufRead, AsyncBufReadExt, AsyncWriteExt};
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AsyncReader: Unpin + Send {
|
||||||
|
async fn read_line(&mut self) -> Result<String, Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
pub trait AsyncWriter: Unpin + Send {
|
||||||
|
async fn write_string(&mut self, buf: String) -> Result<(), Error>;
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T: AsyncBufRead + Unpin + Send> AsyncReader for T {
|
||||||
|
async fn read_line(&mut self) -> Result<String, Error> {
|
||||||
|
let mut buf = Vec::new();
|
||||||
|
let length = AsyncBufReadExt::read_until(self, b'\n', &mut buf).await?;
|
||||||
|
|
||||||
|
if length == 0 {
|
||||||
|
return Err(Error::new(ErrorKind::BrokenPipe, "Server Disconnected"));
|
||||||
|
}
|
||||||
|
|
||||||
|
let string = String::from_utf8_lossy(&buf).to_string();
|
||||||
|
|
||||||
|
Ok(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[async_trait]
|
||||||
|
impl<T: AsyncWriteExt + Unpin + Send> AsyncWriter for T {
|
||||||
|
async fn write_string(&mut self, buf: String) -> Result<(), Error> {
|
||||||
|
self.write_all_buf(&mut buf.as_bytes()).await?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,73 +0,0 @@
|
||||||
use openssl::ssl::{SslConnector, SslMethod};
|
|
||||||
use std::io::{ErrorKind, Write};
|
|
||||||
use std::net::TcpStream;
|
|
||||||
use std::sync::mpsc;
|
|
||||||
use std::thread;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
pub fn write_to_server(
|
|
||||||
server: &str,
|
|
||||||
port: &str,
|
|
||||||
rx: mpsc::Receiver<String>,
|
|
||||||
tx: mpsc::Sender<String>,
|
|
||||||
) {
|
|
||||||
'big: loop {
|
|
||||||
println!("Connecting to {server}:{port}");
|
|
||||||
let tcp_stream =
|
|
||||||
TcpStream::connect(format!("{server}:{port}")).expect("Couldn't connect to server");
|
|
||||||
|
|
||||||
let connector = SslConnector::builder(SslMethod::tls()).unwrap().build();
|
|
||||||
let mut stream = connector
|
|
||||||
.connect(server, &tcp_stream)
|
|
||||||
.expect("Couldn't start TLS");
|
|
||||||
|
|
||||||
stream
|
|
||||||
.get_mut()
|
|
||||||
.set_nonblocking(true)
|
|
||||||
.expect("Failed to set nonblocking");
|
|
||||||
|
|
||||||
loop {
|
|
||||||
let mut buffer: Vec<u8> = Vec::new();
|
|
||||||
let mut buf: [u8; 1] = [0];
|
|
||||||
let newline: u8 = b'\n';
|
|
||||||
|
|
||||||
while buf[0] != newline {
|
|
||||||
match stream.ssl_read(&mut buf) {
|
|
||||||
Ok(_length) => {
|
|
||||||
if _length > 0 {
|
|
||||||
buffer.push(buf[0]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Err(_error) => match _error.io_error() {
|
|
||||||
None => {
|
|
||||||
dbg!(_error.ssl_error());
|
|
||||||
continue 'big;
|
|
||||||
}
|
|
||||||
Some(error) => match error.kind() {
|
|
||||||
ErrorKind::WouldBlock => {}
|
|
||||||
_ => {
|
|
||||||
dbg!(error.kind());
|
|
||||||
println!("Couldn't read the stream");
|
|
||||||
continue 'big;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
}
|
|
||||||
let value = rx.try_recv().unwrap_or("".to_string());
|
|
||||||
match value.as_str() {
|
|
||||||
"DUMMY CLOSE_CONNECTION" => {
|
|
||||||
continue 'big;
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
match stream.write_all(value.as_bytes()) {
|
|
||||||
Ok(_) => {}
|
|
||||||
Err(_e) => println!("Couldn't send {value}"),
|
|
||||||
};
|
|
||||||
thread::sleep(Duration::from_micros(100));
|
|
||||||
}
|
|
||||||
|
|
||||||
let _ = tx.send(dbg!(String::from_utf8_lossy(&buffer).to_string()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
54
test.asc
Normal file
54
test.asc
Normal file
|
@ -0,0 +1,54 @@
|
||||||
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Version: Keybase OpenPGP v2.1.15
|
||||||
|
Comment: https://keybase.io/crypto
|
||||||
|
|
||||||
|
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
|
||||||
|
lRf+/ik=
|
||||||
|
=4/Rx
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
Loading…
Reference in a new issue