WIP: hellfire #1
@ -14,6 +14,7 @@ native-tls = "0.2.11"
|
|||||||
rand = "0.8.5"
|
rand = "0.8.5"
|
||||||
toml = "0.8.10"
|
toml = "0.8.10"
|
||||||
base64 = "0.22.0"
|
base64 = "0.22.0"
|
||||||
|
regex = "1.10.3"
|
||||||
serde = { version = "1.0.197", features = ["derive"] }
|
serde = { version = "1.0.197", features = ["derive"] }
|
||||||
colored = "2.1.0"
|
colored = "2.1.0"
|
||||||
futures = "0.3.30"
|
futures = "0.3.30"
|
||||||
|
44
src/main.rs
44
src/main.rs
@ -22,8 +22,11 @@ struct Config {
|
|||||||
|
|
||||||
mod mods {
|
mod mods {
|
||||||
pub mod sasl;
|
pub mod sasl;
|
||||||
|
pub mod sed;
|
||||||
}
|
}
|
||||||
use mods::sasl::{start_sasl_auth, handle_sasl_messages};
|
use mods::sasl::{start_sasl_auth, handle_sasl_messages};
|
||||||
|
use mods::sed::{SedCommand, MessageBuffer};
|
||||||
|
|
||||||
|
|
||||||
#[tokio::main(flavor = "multi_thread", worker_threads = 12)]
|
#[tokio::main(flavor = "multi_thread", worker_threads = 12)]
|
||||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||||
@ -48,6 +51,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
}).await.unwrap();
|
}).await.unwrap();
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Load the config file
|
/// Load the config file
|
||||||
fn loaded_config() -> Result<Config, Box<dyn std::error::Error>> {
|
fn loaded_config() -> Result<Config, Box<dyn std::error::Error>> {
|
||||||
let config_contents = fs::read_to_string("config.toml")?;
|
let config_contents = fs::read_to_string("config.toml")?;
|
||||||
@ -67,16 +71,15 @@ async fn tls_exec(config: &Config, tcp_stream: TcpStream) -> Result<tokio_native
|
|||||||
async fn handler(tls_stream: tokio_native_tls::TlsStream<TcpStream>, config: Config) -> Result<(), Box<dyn std::error::Error>> {
|
async fn handler(tls_stream: tokio_native_tls::TlsStream<TcpStream>, config: Config) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
let (reader, writer) = split(tls_stream);
|
let (reader, writer) = split(tls_stream);
|
||||||
let (tx, rx) = mpsc::channel(1000);
|
let (tx, rx) = mpsc::channel(1000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
let read_task = tokio::spawn(async move {
|
let read_task = tokio::spawn(async move {
|
||||||
readmsg(reader, tx).await;
|
readmsg(reader, tx).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
|
let message_buffer = MessageBuffer::new(1000);
|
||||||
|
|
||||||
let write_task = tokio::spawn(async move {
|
let write_task = tokio::spawn(async move {
|
||||||
writemsg(writer, rx, &config).await;
|
writemsg(writer, rx, &config, message_buffer).await;
|
||||||
});
|
});
|
||||||
|
|
||||||
let _ = tokio::try_join!(read_task, write_task);
|
let _ = tokio::try_join!(read_task, write_task);
|
||||||
@ -104,10 +107,10 @@ async fn readmsg(mut reader: tokio::io::ReadHalf<tokio_native_tls::TlsStream<Tcp
|
|||||||
|
|
||||||
|
|
||||||
static SASL_AUTH: AtomicBool = AtomicBool::new(false);
|
static SASL_AUTH: AtomicBool = AtomicBool::new(false);
|
||||||
|
|
||||||
/// Write messages to the server
|
/// Write messages to the server
|
||||||
async fn writemsg(mut writer: tokio::io::WriteHalf<tokio_native_tls::TlsStream<TcpStream>>, mut rx: tokio::sync::mpsc::Receiver<String>, config: &Config) {
|
async fn writemsg(mut writer: tokio::io::WriteHalf<tokio_native_tls::TlsStream<TcpStream>>, mut rx: tokio::sync::mpsc::Receiver<String>, config: &Config, mut message_buffer: MessageBuffer) {
|
||||||
// sasl auth
|
|
||||||
//let capabilities = config.capabilities.clone();
|
|
||||||
let username = config.sasl_username.clone().unwrap();
|
let username = config.sasl_username.clone().unwrap();
|
||||||
let password = config.sasl_password.clone().unwrap();
|
let password = config.sasl_password.clone().unwrap();
|
||||||
let nickname = config.nickname.clone();
|
let nickname = config.nickname.clone();
|
||||||
@ -121,12 +124,7 @@ async fn writemsg(mut writer: tokio::io::WriteHalf<tokio_native_tls::TlsStream<T
|
|||||||
nickme(&mut writer, &nickname).await.unwrap();
|
nickme(&mut writer, &nickname).await.unwrap();
|
||||||
writer.flush().await.unwrap();
|
writer.flush().await.unwrap();
|
||||||
}
|
}
|
||||||
//writer.flush().await.unwrap();
|
|
||||||
//let msg = rx.recv().await.unwrap();
|
|
||||||
//let msg = msg.trim();
|
|
||||||
//let parts = msg.split(' ').collect::<Vec<&str>>();
|
|
||||||
|
|
||||||
// THIS NEEDS TO BE REBUILT TO BE MORE MODULAR AND SECURE
|
|
||||||
while let Some(msg) = rx.recv().await {
|
while let Some(msg) = rx.recv().await {
|
||||||
let msg = msg.trim();
|
let msg = msg.trim();
|
||||||
if msg.is_empty() {
|
if msg.is_empty() {
|
||||||
@ -136,8 +134,6 @@ async fn writemsg(mut writer: tokio::io::WriteHalf<tokio_native_tls::TlsStream<T
|
|||||||
let serv = parts.first().unwrap_or(&"");
|
let serv = parts.first().unwrap_or(&"");
|
||||||
let cmd = parts.get(1).unwrap_or(&"");
|
let cmd = parts.get(1).unwrap_or(&"");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
println!("{} {} {} {} {}", "DEBUG:".bold().yellow(), "serv:".bold().green(), serv.purple(), "cmd:".bold().green(), cmd.purple());
|
println!("{} {} {} {} {}", "DEBUG:".bold().yellow(), "serv:".bold().green(), serv.purple(), "cmd:".bold().green(), cmd.purple());
|
||||||
if *serv == "PING" {
|
if *serv == "PING" {
|
||||||
let response = msg.replace("PING", "PONG") + "\r\n";
|
let response = msg.replace("PING", "PONG") + "\r\n";
|
||||||
@ -164,10 +160,23 @@ async fn writemsg(mut writer: tokio::io::WriteHalf<tokio_native_tls::TlsStream<T
|
|||||||
}
|
}
|
||||||
if *cmd == "PRIVMSG" {
|
if *cmd == "PRIVMSG" {
|
||||||
let channel = parts[2];
|
let channel = parts[2];
|
||||||
let user = parts[0];
|
let user = parts[0].strip_prefix(':').unwrap().split_at(parts[0].find('!').unwrap()).0;
|
||||||
let host = user.split_at(user.find('!').unwrap());
|
let host = parts[0].split_at(parts[0].find('!').unwrap()).1;
|
||||||
let msg = parts[3..].join(" ").replace(':', "");
|
let msg_content = parts[3..].join(" ").replace(':', "");
|
||||||
println!("{}{}{} {}{} {} {} {}", "[".green().bold(), ">".yellow().bold(), "]".green().bold(), "PRIVMSG:".bold().yellow(), ":".bold().green(), channel.yellow(), user.blue(), msg.purple());
|
println!("{} {} {} {} {} {} {}", "DEBUG:".bold().yellow(), "channel:".bold().green(), channel.purple(), "user:".bold().green(), host.purple(), "msg:".bold().green(), msg_content.purple());
|
||||||
|
// sed
|
||||||
|
if msg_content.starts_with("s/") {
|
||||||
|
println!("Sed command detected");
|
||||||
|
if let Some(sed_command) = SedCommand::parse(&msg_content) {
|
||||||
|
if let Some(response) = message_buffer.apply_sed_command(&sed_command) {
|
||||||
|
writer.write_all(format!("PRIVMSG {} :{}: {}\r\n", channel, user, response).as_bytes()).await.unwrap();
|
||||||
|
writer.flush().await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
message_buffer.add_message(msg_content);
|
||||||
|
}
|
||||||
|
// other commands here
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -178,3 +187,4 @@ async fn nickme<W: tokio::io::AsyncWriteExt + Unpin>(writer: &mut W, nickname: &
|
|||||||
writer.flush().await?;
|
writer.flush().await?;
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
70
src/mods/sed.rs
Normal file
70
src/mods/sed.rs
Normal file
@ -0,0 +1,70 @@
|
|||||||
|
use regex::Regex;
|
||||||
|
use std::collections::VecDeque;
|
||||||
|
|
||||||
|
pub struct SedCommand {
|
||||||
|
pattern: Regex,
|
||||||
|
replacement: String,
|
||||||
|
global: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl SedCommand {
|
||||||
|
pub fn parse(command: &str) -> Option<Self> {
|
||||||
|
let parts: Vec<&str> = command.split('/').collect();
|
||||||
|
if parts.len() >= 4 {
|
||||||
|
let pattern = match Regex::new(parts[1]) {
|
||||||
|
Ok(regex) => regex,
|
||||||
|
Err(_) => return None,
|
||||||
|
};
|
||||||
|
let replacement = parts[2].to_string();
|
||||||
|
let global = parts.get(3).map_or(false, |&flag| flag.contains('g'));
|
||||||
|
|
||||||
|
Some(SedCommand {
|
||||||
|
pattern,
|
||||||
|
replacement,
|
||||||
|
global,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_to(&self, message: &str) -> String {
|
||||||
|
if self.global {
|
||||||
|
self.pattern.replace_all(message, self.replacement.as_str()).to_string()
|
||||||
|
} else {
|
||||||
|
self.pattern.replace(message, self.replacement.as_str()).to_string()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct MessageBuffer {
|
||||||
|
buffer: VecDeque<String>,
|
||||||
|
capacity: usize,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl MessageBuffer {
|
||||||
|
pub fn new(capacity: usize) -> Self {
|
||||||
|
MessageBuffer {
|
||||||
|
buffer: VecDeque::with_capacity(capacity),
|
||||||
|
capacity,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn add_message(&mut self, message: String) {
|
||||||
|
if self.buffer.len() == self.capacity {
|
||||||
|
self.buffer.pop_front();
|
||||||
|
}
|
||||||
|
self.buffer.push_back(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn apply_sed_command(&mut self, command: &SedCommand) -> Option<String> {
|
||||||
|
for message in self.buffer.iter_mut() {
|
||||||
|
if command.pattern.is_match(message) {
|
||||||
|
*message = command.apply_to(message);
|
||||||
|
return Some(message.clone());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user