Compare commits
No commits in common. "main" and "v1.0.2" have entirely different histories.
|
@ -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"
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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>,
|
||||||
|
|
|
@ -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());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
50
src/main.rs
50
src/main.rs
|
@ -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 {
|
||||||
|
|
|
@ -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(());
|
||||||
|
|
|
@ -6,19 +6,14 @@ 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 {
|
|
||||||
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 connector = SslConnector::builder(SslMethod::tls()).unwrap().build();
|
||||||
let mut stream = connector
|
let mut stream = connector
|
||||||
.connect(server, &tcp_stream)
|
.connect(&server, tcp_stream)
|
||||||
.expect("Couldn't start TLS");
|
.expect("Couldn't start TLS");
|
||||||
|
|
||||||
stream
|
stream
|
||||||
|
@ -41,25 +36,17 @@ pub fn write_to_server(
|
||||||
Err(_error) => match _error.io_error() {
|
Err(_error) => match _error.io_error() {
|
||||||
None => {
|
None => {
|
||||||
dbg!(_error.ssl_error());
|
dbg!(_error.ssl_error());
|
||||||
continue 'big;
|
|
||||||
}
|
}
|
||||||
Some(error) => match error.kind() {
|
Some(error) => match error.kind() {
|
||||||
ErrorKind::WouldBlock => {}
|
ErrorKind::WouldBlock => {}
|
||||||
_ => {
|
_ => {
|
||||||
dbg!(error.kind());
|
dbg!(error.kind());
|
||||||
println!("Couldn't read the stream");
|
println!("Couldn't read the stream");
|
||||||
continue 'big;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
let value = rx.try_recv().unwrap_or("".to_string());
|
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()) {
|
match stream.write_all(value.as_bytes()) {
|
||||||
Ok(_) => {}
|
Ok(_) => {}
|
||||||
Err(_e) => println!("Couldn't send {value}"),
|
Err(_e) => println!("Couldn't send {value}"),
|
||||||
|
@ -70,4 +57,3 @@ pub fn write_to_server(
|
||||||
let _ = tx.send(dbg!(String::from_utf8_lossy(&buffer).to_string()));
|
let _ = tx.send(dbg!(String::from_utf8_lossy(&buffer).to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in a new issue