Explorar el Código

darkirc: add IRC connection password

dasman hace 2 años
padre
commit
16826b2db2

+ 4 - 0
bin/darkirc/src/irc/client.rs

@@ -80,6 +80,8 @@ pub struct Client {
     pub reg_paused: AtomicBool,
     /// CAP END marker
     pub is_cap_end: AtomicBool,
+    /// Password setup marker
+    pub is_pass_set: AtomicBool,
     /// Client username
     pub username: Arc<RwLock<String>>,
     /// Client nickname
@@ -118,6 +120,7 @@ impl Client {
             registered: AtomicBool::new(false),
             reg_paused: AtomicBool::new(false),
             is_cap_end: AtomicBool::new(false),
+            is_pass_set: AtomicBool::new(false),
             username: username.clone(),
             nickname: nickname.clone(),
             realname: RwLock::new(String::from("*")),
@@ -341,6 +344,7 @@ impl Client {
             "NAMES" => self.handle_cmd_names(&args).await?,
             "NICK" => self.handle_cmd_nick(&args).await?,
             "PART" => self.handle_cmd_part(&args).await?,
+            "PASS" => self.handle_cmd_pass(&args).await?,
             "PING" => self.handle_cmd_ping(&args).await?,
             "PRIVMSG" => self.handle_cmd_privmsg(&args).await?,
             "REHASH" => self.handle_cmd_rehash(&args).await?,

+ 43 - 1
bin/darkirc/src/irc/command.rs

@@ -539,7 +539,10 @@ impl Client {
         *self.nickname.write().await = nickname.to_string();
 
         // If the username is set, we can complete the registration
-        if *self.username.read().await != "*" && !self.registered.load(SeqCst) {
+        if *self.username.read().await != "*" &&
+            !self.registered.load(SeqCst) &&
+            self.is_pass_set.load(SeqCst)
+        {
             self.registered.store(true, SeqCst);
             if self.reg_paused.load(SeqCst) {
                 return Ok(vec![])
@@ -604,6 +607,34 @@ impl Client {
         Ok(replies)
     }
 
+    /// `PASS <password>`
+    ///
+    /// Used to set a ‘connection password’. If set, the password must
+    /// be set before USER/NICK commands.
+    pub async fn handle_cmd_pass(&self, args: &str) -> Result<Vec<ReplyType>> {
+        let mut tokens = args.split_ascii_whitespace();
+        let nick = self.nickname.read().await.to_string();
+        let Some(password) = tokens.next() else {
+            // self.penalty.fetch_add(1, SeqCst);
+            return Ok(vec![ReplyType::Server((
+                ERR_NEEDMOREPARAMS,
+                format!("{} PASS :{}", nick, INVALID_SYNTAX),
+            ))])
+        };
+
+        if self.server.password == password.to_string() {
+            self.is_pass_set.store(true, SeqCst);
+        } else {
+            error!("[IRC CLIENT] Password is not correct!");
+            return Ok(vec![ReplyType::Server((
+                ERR_PASSWDMISMATCH,
+                format!("{} PASS :{}", nick, PASSWORD_MISMATCH),
+            ))])
+        }
+
+        Ok(vec![])
+    }
+
     /// `PING <server1>`
     ///
     /// Tests a connection. A PING message results in a PONG reply.
@@ -775,6 +806,11 @@ impl Client {
             ))])
         }
 
+        // If password is not set register user normally
+        if self.server.password.is_empty() {
+            self.is_pass_set.store(true, SeqCst);
+        }
+
         // Parse the line
         let nick = self.nickname.read().await.to_string();
         let mut tokens = args.split_ascii_whitespace();
@@ -827,6 +863,12 @@ impl Client {
 
         // If the nickname is set, we can complete the registration
         if nick != "*" {
+            if !self.is_pass_set.load(SeqCst) {
+                return Ok(vec![ReplyType::Server((
+                    ERR_PASSWDMISMATCH,
+                    format!("{} PASS :{}", nick, PASSWORD_MISMATCH),
+                ))])
+            }
             self.registered.store(true, SeqCst);
             if self.reg_paused.load(SeqCst) {
                 return Ok(vec![])

+ 8 - 0
bin/darkirc/src/irc/rpl.rs

@@ -24,6 +24,8 @@ pub const WELCOME: &str = "Welcome to the DarkIRC network";
 pub const NOT_REGISTERED: &str = "You have not registered";
 /// The message sent to the client when they are already registered
 pub const ALREADY_REGISTERED: &str = "You may not reregister";
+/// The message sent to the client when they enter wrong or no password
+pub const PASSWORD_MISMATCH: &str = "Password incorrect";
 /// The message sent to the client when command params could not parse
 pub const INVALID_SYNTAX: &str = "Syntax error";
 
@@ -198,6 +200,12 @@ pub const ERR_NEEDMOREPARAMS: u16 = 461;
 /// set during registration.
 pub const ERR_ALREADYREGISTERED: u16 = 462;
 
+/// `<client> :Password incorrect`
+///
+/// Returned to indicate that the connection could not be registered
+/// as the password was either incorrect or not supplied.
+pub const ERR_PASSWDMISMATCH: u16 = 464;
+
 /// `<client> :Cant change mode for other users`
 ///
 /// Indicates that a MODE command affecting a user failed because they

+ 4 - 0
bin/darkirc/src/irc/server.rs

@@ -66,6 +66,8 @@ pub struct IrcServer {
     pub contacts: RwLock<HashMap<String, IrcContact>>,
     /// Active client connections
     clients: Mutex<HashMap<u16, StoppableTaskPtr>>,
+    /// IRC server Password
+    pub password: String,
 }
 
 impl IrcServer {
@@ -78,6 +80,7 @@ impl IrcServer {
         tls_cert: Option<String>,
         tls_secret: Option<String>,
         config_path: PathBuf,
+        password: String,
     ) -> Result<Arc<Self>> {
         let scheme = listen.scheme();
         if scheme != "tcp" && scheme != "tcp+tls" {
@@ -128,6 +131,7 @@ impl IrcServer {
             channels: RwLock::new(HashMap::new()),
             contacts: RwLock::new(HashMap::new()),
             clients: Mutex::new(HashMap::new()),
+            password,
         });
 
         // Load any channel/contact configuration.

+ 6 - 0
bin/darkirc/src/main.rs

@@ -112,6 +112,10 @@ struct Args {
     #[structopt(long, default_value = "10")]
     sync_timeout: u8,
 
+    /// IRC Password
+    #[structopt(long)]
+    pub password: Option<String>,
+
     /// P2P network settings
     #[structopt(flatten)]
     net: SettingsOpt,
@@ -288,6 +292,7 @@ async fn realmain(args: Args, ex: Arc<Executor<'static>>) -> Result<()> {
     );
 
     info!("Starting IRC server");
+    let password = args.password.unwrap_or_default();
     let config_path = get_config_path(args.config, CONFIG_FILE)?;
     let irc_server = IrcServer::new(
         darkirc.clone(),
@@ -295,6 +300,7 @@ async fn realmain(args: Args, ex: Arc<Executor<'static>>) -> Result<()> {
         args.irc_tls_cert,
         args.irc_tls_secret,
         config_path,
+        password,
     )
     .await?;