WIP: hellfire #1
@ -14,6 +14,7 @@ native-tls = "0.2.11"
|
||||
rand = "0.8.5"
|
||||
toml = "0.8.10"
|
||||
base64 = "0.22.0"
|
||||
regex = "1.10.3"
|
||||
serde = { version = "1.0.197", features = ["derive"] }
|
||||
colored = "2.1.0"
|
||||
futures = "0.3.30"
|
||||
|
44
src/main.rs
44
src/main.rs
@ -22,8 +22,11 @@ struct Config {
|
||||
|
||||
mod mods {
|
||||
pub mod sasl;
|
||||
pub mod sed;
|
||||
}
|
||||
use mods::sasl::{start_sasl_auth, handle_sasl_messages};
|
||||
use mods::sed::{SedCommand, MessageBuffer};
|
||||
|
||||
|
||||
#[tokio::main(flavor = "multi_thread", worker_threads = 12)]
|
||||
async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
@ -48,6 +51,7 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
||||
}).await.unwrap();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Load the config file
|
||||
fn loaded_config() -> Result<Config, Box<dyn std::error::Error>> {
|
||||
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>> {
|
||||
let (reader, writer) = split(tls_stream);
|
||||
let (tx, rx) = mpsc::channel(1000);
|
||||
|
||||
|
||||
|
||||
let read_task = tokio::spawn(async move {
|
||||
readmsg(reader, tx).await;
|
||||
});
|
||||
|
||||
let message_buffer = MessageBuffer::new(1000);
|
||||
|
||||
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);
|
||||
@ -104,10 +107,10 @@ async fn readmsg(mut reader: tokio::io::ReadHalf<tokio_native_tls::TlsStream<Tcp
|
||||
|
||||
|
||||
static SASL_AUTH: AtomicBool = AtomicBool::new(false);
|
||||
|
||||
/// 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) {
|
||||
// sasl auth
|
||||
//let capabilities = config.capabilities.clone();
|
||||
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) {
|
||||
|
||||
let username = config.sasl_username.clone().unwrap();
|
||||
let password = config.sasl_password.clone().unwrap();
|
||||
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();
|
||||
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 {
|
||||
let msg = msg.trim();
|
||||
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 cmd = parts.get(1).unwrap_or(&"");
|
||||
|
||||
|
||||
|
||||
println!("{} {} {} {} {}", "DEBUG:".bold().yellow(), "serv:".bold().green(), serv.purple(), "cmd:".bold().green(), cmd.purple());
|
||||
if *serv == "PING" {
|
||||
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" {
|
||||
let channel = parts[2];
|
||||
let user = parts[0];
|
||||
let host = user.split_at(user.find('!').unwrap());
|
||||
let msg = parts[3..].join(" ").replace(':', "");
|
||||
println!("{}{}{} {}{} {} {} {}", "[".green().bold(), ">".yellow().bold(), "]".green().bold(), "PRIVMSG:".bold().yellow(), ":".bold().green(), channel.yellow(), user.blue(), msg.purple());
|
||||
let user = parts[0].strip_prefix(':').unwrap().split_at(parts[0].find('!').unwrap()).0;
|
||||
let host = parts[0].split_at(parts[0].find('!').unwrap()).1;
|
||||
let msg_content = parts[3..].join(" ").replace(':', "");
|
||||
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?;
|
||||
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