Compare commits

..

No commits in common. "main" and "v1.0.2" have entirely different histories.
main ... v1.0.2

8 changed files with 78 additions and 166 deletions

View file

@ -1,11 +1,7 @@
[package] [package]
name = "e2e-irc" name = "e2e-irc"
version = "3.0.0" version = "1.0.2"
edition = "2021" edition = "2021"
license = "GPL-3.0"
keywords = ["irc", "encryption"]
description = "An IRC bouncer that can send encrypted messages"
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
@ -14,7 +10,7 @@ argparse = "0.2.2"
base64 = "0.21.4" base64 = "0.21.4"
dirs = "5.0.1" dirs = "5.0.1"
eyre = "0.6.8" eyre = "0.6.8"
ircparser-vanten = "0.2.1" ircparser = "0.2.1"
openssl = "0.10" openssl = "0.10"
pgp = "0.10.2" pgp = "0.10.2"
rand = "0.8.5" rand = "0.8.5"

View file

@ -26,7 +26,7 @@ Optional arguments:
# Install # Install
```bash ```bash
cargo install e2e-irc cargo install --git https://forgejo.vanten-s.com/vanten-s/e2e-irc.git
``` ```
# Run # Run

View file

@ -1,41 +1,10 @@
use crate::helpers::bytes_to_privmsg_base64; use crate::helpers::bytes_to_privmsg_base64;
use crate::{encryption, helpers, State}; use crate::{encryption, helpers};
use eyre::Result; use eyre::Result;
use pgp::{Deserializable, SignedPublicKey}; use pgp::{Deserializable, SignedPublicKey};
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::mpsc::{Receiver, Sender}; use std::sync::mpsc::{Receiver, Sender};
#[derive(Debug)]
struct InvalidCommand;
impl std::fmt::Display for InvalidCommand {
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(' ');
match unwrap_option!(splitted.next()) {
"ALLOW_UNENCRYPTED" => state
.nicks_without_encryption
.push(unwrap_option!(splitted.next()).to_string().to_lowercase()),
_ => return Err(InvalidCommand.into()),
};
Ok(())
}
pub fn handle_message_from_client( pub fn handle_message_from_client(
recieved: &str, recieved: &str,
public_key: &Vec<u8>, public_key: &Vec<u8>,
@ -45,34 +14,10 @@ pub fn handle_message_from_client(
writer_channel_rx: &Receiver<String>, writer_channel_rx: &Receiver<String>,
listener_channel_tx: &Sender<String>, listener_channel_tx: &Sender<String>,
_listener_channel_rx: &Receiver<String>, _listener_channel_rx: &Receiver<String>,
state: &mut State,
) -> Result<()> { ) -> Result<()> {
let mut recieved = recieved.to_string(); let command = &ircparser::parse(recieved).expect("Got an invalid IRC instruction")[0];
if recieved.split(' ').count() == 1 {
recieved += " ";
}
let parsed = ircparser::parse(&recieved);
let command = match parsed {
Ok(val) => val[0].clone(),
Err(_) => {
writer_channel_tx.send(recieved)?;
return Ok(());
}
};
if command.command == "PRIVMSG" && !command.params[0].starts_with('#') { if command.command == "PRIVMSG" && !command.params[0].starts_with('#') {
if command.params[0] == "BOUNCER" {
return parse_bouncer_command(command.params[1].clone(), state);
}
if state
.nicks_without_encryption
.contains(&command.params[0].to_lowercase())
{
writer_channel_tx.send(recieved)?;
return Ok(());
}
let other = &command.params[0]; let other = &command.params[0];
if !keys.contains_key(other) { if !keys.contains_key(other) {

View file

@ -3,7 +3,7 @@ use eyre::Result;
use std::sync::mpsc::{self, Receiver, Sender}; use std::sync::mpsc::{self, Receiver, Sender};
#[derive(Debug)] #[derive(Debug)]
pub struct IrcParseError; struct IrcParseError;
impl std::fmt::Display for IrcParseError { impl std::fmt::Display for IrcParseError {
fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, _: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@ -35,22 +35,6 @@ macro_rules! unwrap_or_return_option {
}; };
} }
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( fn forward(
message: String, message: String,
stream: &Sender<String>, stream: &Sender<String>,

View file

@ -54,6 +54,5 @@ pub fn listen_to_client(tx: mpsc::Sender<String>, rx: mpsc::Receiver<String>, po
stream_handler(&tx, &rx, stream); stream_handler(&tx, &rx, stream);
println!("Closed connection with {ip}"); println!("Closed connection with {ip}");
let _ = tx.send("DUMMY CLOSE_CONNECTION".to_string());
} }
} }

View file

@ -1,10 +1,10 @@
use argparse::{ArgumentParser, Store}; use argparse::{ArgumentParser, Store};
use dirs::config_local_dir; use dirs::config_local_dir;
use eyre::Result; use eyre::Result;
use helpers::State;
use pgp::{Deserializable, SignedPublicKey, SignedSecretKey}; use pgp::{Deserializable, SignedPublicKey, SignedSecretKey};
use std::collections::HashMap; use std::collections::HashMap;
use std::fs; use std::fs;
use std::net::{Shutdown, TcpStream};
use std::sync::mpsc; use std::sync::mpsc;
use std::thread; use std::thread;
use std::time::Duration; use std::time::Duration;
@ -55,11 +55,7 @@ fn main() -> Result<()> {
let mut ap = ArgumentParser::new(); let mut ap = ArgumentParser::new();
ap.set_description("Encrypted IRC Bouncer"); ap.set_description("Encrypted IRC Bouncer");
ap.refer(&mut server) ap.refer(&mut server)
.add_argument( .add_argument("server", Store, "The Address Of The Server The Bouncer Connects To")
"server",
Store,
"The Address Of The Server The Bouncer Connects To",
)
.required(); .required();
ap.refer(&mut port) ap.refer(&mut port)
.add_option(&["-p", "--port"], Store, "The Port The Bouncer Binds To"); .add_option(&["-p", "--port"], Store, "The Port The Bouncer Binds To");
@ -71,36 +67,50 @@ fn main() -> Result<()> {
ap.parse_args_or_exit(); ap.parse_args_or_exit();
} }
let server = &server;
let stream = TcpStream::connect(format!("{server}:{server_port}"))?;
let public_key = fs::read(public_key_location)?; let public_key = fs::read(public_key_location)?;
let secret_key = SignedSecretKey::from_bytes(fs::read(secret_key_location)?.as_slice())?; let secret_key = SignedSecretKey::from_bytes(fs::read(secret_key_location)?.as_slice())?;
let reader_stream = match stream.try_clone() {
Ok(stream) => stream,
Err(_error) => {
let _ = stream.shutdown(Shutdown::Both);
panic!("Failed to create the reader stream")
}
};
let writer_stream = match stream.try_clone() {
Ok(stream) => stream,
Err(_error) => {
let _ = stream.shutdown(Shutdown::Both);
let _ = reader_stream.shutdown(Shutdown::Both);
panic!("Failed to create the writer stream")
}
};
let (listener_channel_send_tx, listener_channel_rx) = mpsc::channel(); let (listener_channel_send_tx, listener_channel_rx) = mpsc::channel();
let (listener_channel_tx, listener_channel_recv_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_tx, writer_channel_send_rx) = mpsc::channel();
let (writer_channel_recv_tx, writer_channel_rx) = mpsc::channel(); let (writer_channel_recv_tx, writer_channel_rx) = mpsc::channel();
let tmp_port = port.clone();
thread::spawn(move || { thread::spawn(move || {
listener_server::listen_to_client( listener_server::listen_to_client(listener_channel_send_tx, listener_channel_recv_rx, port)
listener_channel_send_tx,
listener_channel_recv_rx,
tmp_port,
)
}); });
let tmp_port = server_port.clone();
let tmp_server = server.clone(); let tmp_server = server.clone();
thread::spawn(move || { thread::spawn(|| {
writer_client::write_to_server( writer_client::write_to_server(
&tmp_server, writer_stream,
&tmp_port, tmp_server,
writer_channel_send_rx, writer_channel_send_rx,
writer_channel_recv_tx, writer_channel_recv_tx,
) )
}); });
let mut keys: HashMap<String, SignedPublicKey> = HashMap::new(); let mut keys: HashMap<String, SignedPublicKey> = HashMap::new();
let mut state = State::new();
loop { loop {
match listener_channel_rx.try_recv() { match listener_channel_rx.try_recv() {
@ -108,13 +118,12 @@ fn main() -> Result<()> {
let _ = client_handler::handle_message_from_client( let _ = client_handler::handle_message_from_client(
&message, &message,
&public_key, &public_key,
&server, server,
&mut keys, &mut keys,
&writer_channel_tx, &writer_channel_tx,
&writer_channel_rx, &writer_channel_rx,
&listener_channel_tx, &listener_channel_tx,
&listener_channel_rx, &listener_channel_rx,
&mut state,
); );
} }
Err(error) => match error { Err(error) => match error {
@ -129,14 +138,13 @@ fn main() -> Result<()> {
&message, &message,
&public_key, &public_key,
&secret_key, &secret_key,
&server, server,
passwd, passwd,
&mut keys, &mut keys,
&writer_channel_tx, &writer_channel_tx,
&writer_channel_rx, &writer_channel_rx,
&listener_channel_tx, &listener_channel_tx,
&listener_channel_rx, &listener_channel_rx,
&state,
); );
} }
Err(error) => match error { Err(error) => match error {

View file

@ -1,6 +1,5 @@
use crate::unwrap_or_return_option; use crate::unwrap_or_return_option;
use crate::unwrap_or_return_result; use crate::unwrap_or_return_result;
use crate::State;
use crate::{encryption, helpers}; use crate::{encryption, helpers};
use eyre::Result; use eyre::Result;
use pgp::{Deserializable, SignedPublicKey, SignedSecretKey}; use pgp::{Deserializable, SignedPublicKey, SignedSecretKey};
@ -26,20 +25,15 @@ pub fn handle_message_from_server(
writer_channel_rx: &Receiver<String>, writer_channel_rx: &Receiver<String>,
listener_channel_tx: &Sender<String>, listener_channel_tx: &Sender<String>,
_listener_channel_rx: &Receiver<String>, _listener_channel_rx: &Receiver<String>,
state: &State,
) -> Result<()> { ) -> Result<()> {
let recieved_parsed = &unwrap_or_return_result!(ircparser::parse(recieved))[0]; 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" if recieved_parsed.command != "PRIVMSG"
|| reciever.starts_with('#') || recieved_parsed
|| state.nicks_without_encryption.contains(reciever) .params
.get(0)
.unwrap_or(&String::new())
.starts_with('#')
{ {
forward(recieved, listener_channel_tx, server)?; forward(recieved, listener_channel_tx, server)?;
return Ok(()); return Ok(());

View file

@ -6,68 +6,54 @@ use std::thread;
use std::time::Duration; use std::time::Duration;
pub fn write_to_server( pub fn write_to_server(
server: &str, tcp_stream: TcpStream,
port: &str, server: String,
rx: mpsc::Receiver<String>, rx: mpsc::Receiver<String>,
tx: mpsc::Sender<String>, tx: mpsc::Sender<String>,
) { ) {
'big: loop { let connector = SslConnector::builder(SslMethod::tls()).unwrap().build();
println!("Connecting to {server}:{port}"); let mut stream = connector
let tcp_stream = .connect(&server, tcp_stream)
TcpStream::connect(format!("{server}:{port}")).expect("Couldn't connect to server"); .expect("Couldn't start TLS");
let connector = SslConnector::builder(SslMethod::tls()).unwrap().build(); stream
let mut stream = connector .get_mut()
.connect(server, &tcp_stream) .set_nonblocking(true)
.expect("Couldn't start TLS"); .expect("Failed to set nonblocking");
stream loop {
.get_mut() let mut buffer: Vec<u8> = Vec::new();
.set_nonblocking(true) let mut buf: [u8; 1] = [0];
.expect("Failed to set nonblocking"); let newline: u8 = b'\n';
loop { while buf[0] != newline {
let mut buffer: Vec<u8> = Vec::new(); match stream.ssl_read(&mut buf) {
let mut buf: [u8; 1] = [0]; Ok(_length) => {
let newline: u8 = b'\n'; if _length > 0 {
buffer.push(buf[0]);
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 => { Err(_error) => match _error.io_error() {
dbg!(_error.ssl_error()); None => {
continue 'big; dbg!(_error.ssl_error());
}
Some(error) => match error.kind() {
ErrorKind::WouldBlock => {}
_ => {
dbg!(error.kind());
println!("Couldn't read the stream");
} }
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 value = rx.try_recv().unwrap_or("".to_string());
let _ = tx.send(dbg!(String::from_utf8_lossy(&buffer).to_string())); 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()));
} }
} }