base caps+sasl implementation
This commit is contained in:
parent
087cef1ffe
commit
e83663ffe5
@ -13,6 +13,7 @@ tokio-native-tls = "0.3.1"
|
|||||||
native-tls = "0.2.11"
|
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"
|
||||||
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"
|
||||||
|
@ -2,5 +2,7 @@ server = "irc.supernets.org"
|
|||||||
port = 6697
|
port = 6697
|
||||||
use_ssl = true
|
use_ssl = true
|
||||||
nickname = "g1r"
|
nickname = "g1r"
|
||||||
channel = "#superbowl"
|
channel = "#dev"
|
||||||
|
sasl_username = "g1r"
|
||||||
|
sasl_password = "fuckyou.lol"
|
||||||
|
capabilities = ["sasl"]
|
||||||
|
42
src/main.rs
42
src/main.rs
@ -14,8 +14,16 @@ struct Config {
|
|||||||
use_ssl: bool,
|
use_ssl: bool,
|
||||||
nickname: String,
|
nickname: String,
|
||||||
channel: String,
|
channel: String,
|
||||||
|
sasl_username: Option<String>,
|
||||||
|
sasl_password: Option<String>,
|
||||||
|
capabilities: Option<Vec<String>>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
mod mods {
|
||||||
|
pub mod sasl;
|
||||||
|
}
|
||||||
|
use mods::sasl::{start_sasl_auth, handle_sasl_messages};
|
||||||
|
|
||||||
#[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>> {
|
||||||
println!("Loading Config...");
|
println!("Loading Config...");
|
||||||
@ -39,13 +47,12 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
|
|
||||||
let (reader, writer) = split(tls_stream);
|
let (reader, writer) = split(tls_stream);
|
||||||
let (tx, mut rx) = mpsc::channel(1000);
|
let (tx, mut rx) = mpsc::channel(1000);
|
||||||
// Spawn a task to handle reading
|
|
||||||
let read_task = tokio::spawn(async move {
|
let read_task = tokio::spawn(async move {
|
||||||
let mut reader = reader;
|
let mut reader = reader;
|
||||||
let mut buf = vec![0; 4096];
|
let mut buf = vec![0; 4096];
|
||||||
loop {
|
loop {
|
||||||
let n = match reader.read(&mut buf).await {
|
let n = match reader.read(&mut buf).await {
|
||||||
Ok(0) => return, // connection was closed
|
Ok(0) => return, // connection has been killed x.x
|
||||||
Ok(n) => n,
|
Ok(n) => n,
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
eprintln!("Error reading from socket: {:?}", e);
|
eprintln!("Error reading from socket: {:?}", e);
|
||||||
@ -58,19 +65,44 @@ async fn main() -> Result<(), Box<dyn std::error::Error>> {
|
|||||||
eprintln!("Error sending message to the channel");
|
eprintln!("Error sending message to the channel");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
if msg.contains("AUTHENTICATE") || msg.contains("903") {
|
||||||
|
handle_sasl_messages(&mut writer, &msg, &config.sasl_username.unwrap(), &config.sasl_password.unwrap()).await.unwrap();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
//let msg = String::from_utf8_lossy(&buf[..n]).to_string();
|
||||||
|
//if msg.contains("AUTHENTICATE") || msg.contains("903") {
|
||||||
|
// handle_sasl_messages(&mut writer, &msg, &sasl_username.unwrap(), &sasl_password.unwrap()).await;
|
||||||
|
//}
|
||||||
});
|
});
|
||||||
|
// capabilities
|
||||||
|
let mut capabilities = config.capabilities.clone().unwrap_or_else(Vec::new);
|
||||||
|
//if config.capabilities.is_some() && config.sasl_password.is_some() && config.sasl_username.is_some() {
|
||||||
|
if config.sasl_username.is_some() && config.sasl_password.is_some() && !capabilities.contains(&"sasl".to_string()) {
|
||||||
|
capabilities.push("sasl".to_string());
|
||||||
|
}
|
||||||
|
|
||||||
let write_task = tokio::spawn(async move {
|
let write_task = tokio::spawn(async move {
|
||||||
let mut writer = writer;
|
let mut writer = writer;
|
||||||
writer.write_all(format!("NICK {}\r\n", config.nickname).as_bytes()).await.unwrap();
|
|
||||||
writer.write_all(format!("USER {} 0 * :{}\r\n", config.nickname, config.nickname).as_bytes()).await.unwrap();
|
// capabilities
|
||||||
writer.write_all(format!("JOIN {}\r\n", config.channel).as_bytes()).await.unwrap();
|
//let capabilities = config.capabilities.clone().unwrap_or_else(Vec::new);
|
||||||
|
if !capabilities.is_empty() {
|
||||||
|
let cap_req = format!("CAP REQ :{}\r\n", capabilities.join(" "));
|
||||||
|
writer.write_all(cap_req.as_bytes()).await.unwrap();
|
||||||
|
// handle that CAP ACK yo
|
||||||
|
} // proceeding with sasl if creds are availble
|
||||||
|
if let (Some(sasl_username), Some(sasl_password)) = (&config.sasl_username, &config.sasl_password) {
|
||||||
|
start_sasl_auth(&mut writer, "PLAIN", &config.nickname, &capabilities).await.unwrap();
|
||||||
|
} else {
|
||||||
|
writer.write_all(format!("NICK {}\r\n", config.nickname).as_bytes()).await.unwrap();
|
||||||
|
writer.write_all(format!("USER {} 0 * :{}\r\n", config.nickname, config.nickname).as_bytes()).await.unwrap();
|
||||||
|
}
|
||||||
writer.flush().await.unwrap();
|
writer.flush().await.unwrap();
|
||||||
|
|
||||||
while let Some(msg) = rx.recv().await {
|
while let Some(msg) = rx.recv().await {
|
||||||
// handle messages better
|
// handle messages better
|
||||||
println!("{} {}", "[%] DEBUG:".bold().green(), msg.purple());
|
println!("{} {}", "[%] DEBUG:".bold().green(), msg.purple());
|
||||||
|
|
||||||
if msg.starts_with("PING") {
|
if msg.starts_with("PING") {
|
||||||
writer.write_all(format!("PONG {}\r\n", &msg[5..]).as_bytes()).await.unwrap();
|
writer.write_all(format!("PONG {}\r\n", &msg[5..]).as_bytes()).await.unwrap();
|
||||||
}
|
}
|
||||||
|
54
src/mods/sasl.rs
Normal file
54
src/mods/sasl.rs
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
// mods/sasl.rs
|
||||||
|
use base64::Engine;
|
||||||
|
use tokio::io::AsyncWriteExt;
|
||||||
|
/// Sends the initial commands to negotiate capabilities and start SASL authentication.
|
||||||
|
pub async fn start_sasl_auth<W: tokio::io::AsyncWriteExt + Unpin>(
|
||||||
|
//pub async fn start_sasl_auth(...) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
writer: &mut W,
|
||||||
|
mechanism: &str,
|
||||||
|
nickname: &str,
|
||||||
|
capabilities: &[String], // Add a parameter for capabilities
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
// Request a list of capabilities from the server
|
||||||
|
writer.write_all(b"CAP LS 302\r\n").await?;
|
||||||
|
|
||||||
|
// Send NICK and USER commands
|
||||||
|
let nick_cmd = format!("NICK {}\r\n", nickname);
|
||||||
|
writer.write_all(nick_cmd.as_bytes()).await?;
|
||||||
|
let user_cmd = format!("USER {} 0 * :{}\r\n", nickname, nickname);
|
||||||
|
writer.write_all(user_cmd.as_bytes()).await?;
|
||||||
|
|
||||||
|
// Request specific capabilities, including 'sasl' for SASL authentication
|
||||||
|
if !capabilities.is_empty() {
|
||||||
|
let cap_req_cmd = format!("CAP REQ :{}\r\n", capabilities.join(" "));
|
||||||
|
writer.write_all(cap_req_cmd.as_bytes()).await?;
|
||||||
|
} else {
|
||||||
|
// If no specific capabilities are requested, directly request 'sasl'
|
||||||
|
writer.write_all(b"CAP REQ :sasl\r\n").await?;
|
||||||
|
}
|
||||||
|
|
||||||
|
writer.flush().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Continues the SASL authentication process based on the server's responses.
|
||||||
|
//pub async fn handle_sasl_messages(...) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
pub async fn handle_sasl_messages<W: tokio::io::AsyncWriteExt + Unpin>(
|
||||||
|
writer: &mut W,
|
||||||
|
message: &str,
|
||||||
|
username: &str,
|
||||||
|
password: &str,
|
||||||
|
) -> Result<(), Box<dyn std::error::Error>> {
|
||||||
|
if message.contains("CAP * ACK :sasl") {
|
||||||
|
writer.write_all(b"AUTHENTICATE PLAIN\r\n").await?;
|
||||||
|
} else if message.starts_with("AUTHENTICATE +") {
|
||||||
|
let auth_string = format!("\0{}\0{}", username, password);
|
||||||
|
let encoded = base64::engine::general_purpose::STANDARD.encode(auth_string);
|
||||||
|
writer.write_all(format!("AUTHENTICATE {}\r\n", encoded).as_bytes()).await?;
|
||||||
|
} else if message.contains("903 * :SASL authentication successful") {
|
||||||
|
writer.write_all(b"CAP END\r\n").await?;
|
||||||
|
}
|
||||||
|
writer.flush().await?;
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user