Compare commits
3 commits
e19e3b0d25
...
d38da05d05
Author | SHA1 | Date | |
---|---|---|---|
vanten-s | d38da05d05 | ||
vanten-s | 83b10eb3d5 | ||
vanten-s | b480ce09e5 |
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -1,4 +1,2 @@
|
||||||
/target
|
/target
|
||||||
secret.gpg
|
Cargo.lock
|
||||||
public.gpg
|
|
||||||
.env
|
|
||||||
|
|
1406
Cargo.lock
generated
1406
Cargo.lock
generated
File diff suppressed because it is too large
Load diff
|
@ -1,15 +1,17 @@
|
||||||
[package]
|
[package]
|
||||||
name = "e2e-irc"
|
name = "e2e-irc"
|
||||||
version = "0.1.0"
|
version = "1.0.2"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
# 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"
|
||||||
base64 = "0.21.4"
|
base64 = "0.21.4"
|
||||||
dotenv = "0.15.0"
|
dirs = "5.0.1"
|
||||||
eyre = "0.6.8"
|
eyre = "0.6.8"
|
||||||
ircparser = "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"
|
||||||
|
toml = "0.8.2"
|
||||||
|
|
42
README.md
42
README.md
|
@ -1,25 +1,35 @@
|
||||||
# e2e-irc
|
# e2e-irc
|
||||||
This is an IRC bouncer that supports end-to-end encryption and is horribly written.
|
This is an IRC bouncer that supports end-to-end encryption and is horribly written.
|
||||||
|
|
||||||
# Setup CWD
|
# Configuration
|
||||||
To use this you need to build it and in your CWD you need to add two files. One called `secret.gpg` and one called `public.gpg` that are just pgp keys.
|
e2e-irc uses a config file in `~/.config/e2e-irc/config.toml` on linux.
|
||||||
You also need a .env file in your CWD like this:
|
The configuration file is a toml file with two or three fields:
|
||||||
|
- public_key the location of the public pgp key
|
||||||
|
- secret_key the location of the secret pgp key
|
||||||
|
- passwd the password of the pgp key
|
||||||
|
|
||||||
|
# Usage
|
||||||
|
```
|
||||||
|
e2e-irc [OPTIONS] SERVER
|
||||||
|
|
||||||
|
Encrypted IRC Bouncer
|
||||||
|
|
||||||
|
Positional arguments:
|
||||||
|
server The Address Of The Server The Bouncer Connects To
|
||||||
|
|
||||||
|
Optional arguments:
|
||||||
|
-h,--help Show this help message and exit
|
||||||
|
-p,--port PORT The Port The Bouncer Binds To
|
||||||
|
--sp,--server-port SERVER_PORT
|
||||||
|
The TLS Enabled Port Of The Server
|
||||||
|
```
|
||||||
|
|
||||||
|
# Install
|
||||||
```bash
|
```bash
|
||||||
SERVER={address of the server you want to connect to (has to support tls)}
|
cargo install --git https://forgejo.vanten-s.com/vanten-s/e2e-irc.git
|
||||||
PORT={the port you want the program to bind to and the port that the IRC client should connect to}
|
|
||||||
PASSWD={password of your pgp key} # Optional
|
|
||||||
```
|
```
|
||||||
|
|
||||||
# Run
|
# Run
|
||||||
Then you just run the binary with:
|
|
||||||
`cargo run --release`
|
|
||||||
or build the project to get a binary
|
|
||||||
```bash
|
```bash
|
||||||
cargo build --release
|
e2e-irc [OPTIONS] SERVER
|
||||||
cp target/release/e2e-irc ~/.local/bin/
|
|
||||||
```
|
```
|
||||||
and after running that once you can run the program with
|
|
||||||
```bash
|
|
||||||
e2e-irc
|
|
||||||
```
|
|
||||||
but you have to have the .env in the CWD
|
|
||||||
|
|
|
@ -17,7 +17,7 @@ pub fn handle_message_from_client(
|
||||||
) -> Result<()> {
|
) -> Result<()> {
|
||||||
let command = &ircparser::parse(recieved).expect("Got an invalid IRC instruction")[0];
|
let command = &ircparser::parse(recieved).expect("Got an invalid IRC instruction")[0];
|
||||||
|
|
||||||
if command.command == "PRIVMSG" && !command.params[0].starts_with("#") {
|
if command.command == "PRIVMSG" && !command.params[0].starts_with('#') {
|
||||||
let other = &command.params[0];
|
let other = &command.params[0];
|
||||||
|
|
||||||
if !keys.contains_key(other) {
|
if !keys.contains_key(other) {
|
||||||
|
@ -27,7 +27,7 @@ pub fn handle_message_from_client(
|
||||||
listener_channel_tx,
|
listener_channel_tx,
|
||||||
"127.0.0.1",
|
"127.0.0.1",
|
||||||
server,
|
server,
|
||||||
&other,
|
other,
|
||||||
"END_KEY",
|
"END_KEY",
|
||||||
)?;
|
)?;
|
||||||
let key = SignedPublicKey::from_bytes(key.as_slice())?;
|
let key = SignedPublicKey::from_bytes(key.as_slice())?;
|
||||||
|
@ -38,12 +38,12 @@ pub fn handle_message_from_client(
|
||||||
|
|
||||||
writer_channel_tx.send(format!("PRIVMSG {other} START_MESSAGE\r\n"))?;
|
writer_channel_tx.send(format!("PRIVMSG {other} START_MESSAGE\r\n"))?;
|
||||||
writer_channel_tx.send(bytes_to_privmsg_base64(
|
writer_channel_tx.send(bytes_to_privmsg_base64(
|
||||||
&encryption::encrypt(&foreign_key, &command.params[1])?,
|
&encryption::encrypt(foreign_key, &command.params[1])?,
|
||||||
other,
|
other,
|
||||||
))?;
|
))?;
|
||||||
writer_channel_tx.send(format!("PRIVMSG {other} END_MESSAGE\r\n"))?;
|
writer_channel_tx.send(format!("PRIVMSG {other} END_MESSAGE\r\n"))?;
|
||||||
} else {
|
} else {
|
||||||
writer_channel_tx.send(recieved.replace("127.0.0.1", &server))?;
|
writer_channel_tx.send(recieved.replace("127.0.0.1", server))?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,7 @@ pub fn encrypt(key: &SignedPublicKey, message: &str) -> Result<Vec<u8>, pgp::err
|
||||||
|
|
||||||
let message = message.encrypt_to_keys(&mut rng, SymmetricKeyAlgorithm::AES128, &[key])?;
|
let message = message.encrypt_to_keys(&mut rng, SymmetricKeyAlgorithm::AES128, &[key])?;
|
||||||
|
|
||||||
Ok(message.to_bytes()?)
|
message.to_bytes()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn decrypt<'a>(
|
pub fn decrypt<'a>(
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
use base64::{engine::general_purpose, Engine as _};
|
use base64::{engine::general_purpose, Engine as _};
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use ircparser;
|
|
||||||
use std::sync::mpsc::{self, Receiver, Sender};
|
use std::sync::mpsc::{self, Receiver, Sender};
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
@ -45,7 +44,7 @@ fn forward(
|
||||||
match ircparser::parse(&message) {
|
match ircparser::parse(&message) {
|
||||||
Ok(val) => match val[0].command.as_str() {
|
Ok(val) => match val[0].command.as_str() {
|
||||||
"PRIVMSG" => stream.send(message),
|
"PRIVMSG" => stream.send(message),
|
||||||
_ => stream.send(message.replace(&server_local, server_forward)),
|
_ => stream.send(message.replace(server_local, server_forward)),
|
||||||
},
|
},
|
||||||
Err(_) => stream.send(message.replace(server_local, server_forward)),
|
Err(_) => stream.send(message.replace(server_local, server_forward)),
|
||||||
}
|
}
|
||||||
|
@ -113,7 +112,7 @@ pub fn recieve_message_base64(
|
||||||
.clone()
|
.clone()
|
||||||
.unwrap_or("".to_string())
|
.unwrap_or("".to_string())
|
||||||
.starts_with(&begin_source_reciever)
|
.starts_with(&begin_source_reciever)
|
||||||
|| recieved.params[0].starts_with("#")
|
|| recieved.params[0].starts_with('#')
|
||||||
{
|
{
|
||||||
forward(recieved_raw, forward_stream, server_local, server_forward)?;
|
forward(recieved_raw, forward_stream, server_local, server_forward)?;
|
||||||
continue;
|
continue;
|
||||||
|
|
|
@ -31,13 +31,13 @@ fn stream_handler(tx: &mpsc::Sender<String>, rx: &mpsc::Receiver<String>, mut st
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
Err(TryRecvError::Empty) => {},
|
Err(TryRecvError::Empty) => {}
|
||||||
Err(TryRecvError::Disconnected) => return,
|
Err(TryRecvError::Disconnected) => return,
|
||||||
}
|
}
|
||||||
thread::sleep(Duration::from_micros(100));
|
thread::sleep(Duration::from_micros(100));
|
||||||
}
|
}
|
||||||
|
|
||||||
let _ = tx.send(String::from_utf8_lossy(&buffer).to_string());
|
let _ = tx.send(dbg!(String::from_utf8_lossy(&buffer).to_string()));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
64
src/main.rs
64
src/main.rs
|
@ -1,4 +1,5 @@
|
||||||
use dotenv::{dotenv, vars};
|
use argparse::{ArgumentParser, Store};
|
||||||
|
use dirs::config_local_dir;
|
||||||
use eyre::Result;
|
use eyre::Result;
|
||||||
use pgp::{Deserializable, SignedPublicKey, SignedSecretKey};
|
use pgp::{Deserializable, SignedPublicKey, SignedSecretKey};
|
||||||
use std::collections::HashMap;
|
use std::collections::HashMap;
|
||||||
|
@ -7,6 +8,7 @@ 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;
|
||||||
|
use toml::Table;
|
||||||
|
|
||||||
mod client_handler;
|
mod client_handler;
|
||||||
mod encryption;
|
mod encryption;
|
||||||
|
@ -16,29 +18,61 @@ mod server_handler;
|
||||||
mod writer_client;
|
mod writer_client;
|
||||||
|
|
||||||
fn main() -> Result<()> {
|
fn main() -> Result<()> {
|
||||||
dotenv().expect("Couldn't load .env. It probably doesn't exist");
|
let config_file = config_local_dir()
|
||||||
let mut vars_hashmap = HashMap::new();
|
.expect("Couldn't get config directory")
|
||||||
|
.join("e2e-irc/config.toml");
|
||||||
|
|
||||||
for var in vars() {
|
if !config_file.exists() {
|
||||||
vars_hashmap.insert(var.0, var.1);
|
panic!("Create a config file at {}", config_file.display());
|
||||||
}
|
}
|
||||||
|
|
||||||
let server = &vars_hashmap["SERVER"];
|
let parsed_config = String::from_utf8_lossy(&fs::read(config_file)?).parse::<Table>()?;
|
||||||
|
|
||||||
let default_passwd = String::new();
|
let public_key_location = parsed_config
|
||||||
|
.get("public_key")
|
||||||
|
.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 port = match vars_hashmap.get("PORT") {
|
let default_password = toml::Value::String(String::new());
|
||||||
Some(val) => val,
|
|
||||||
None => "6666",
|
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();
|
||||||
}
|
}
|
||||||
.to_string();
|
|
||||||
|
|
||||||
let passwd = vars_hashmap.get("PASSWD").unwrap_or(&default_passwd);
|
let server = &server;
|
||||||
|
|
||||||
let stream = TcpStream::connect(format!("{server}:6697"))?;
|
let stream = TcpStream::connect(format!("{server}:{server_port}"))?;
|
||||||
|
|
||||||
let public_key = fs::read("public.gpg")?;
|
let public_key = fs::read(public_key_location)?;
|
||||||
let secret_key = SignedSecretKey::from_bytes(fs::read("secret.gpg")?.as_slice())?;
|
let secret_key = SignedSecretKey::from_bytes(fs::read(secret_key_location)?.as_slice())?;
|
||||||
|
|
||||||
let reader_stream = match stream.try_clone() {
|
let reader_stream = match stream.try_clone() {
|
||||||
Ok(stream) => stream,
|
Ok(stream) => stream,
|
||||||
|
|
Loading…
Reference in a new issue