server.rs 9.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. use std::str::FromStr;
  2. use async_std::net::TcpStream;
  3. use futures::{io::WriteHalf, AsyncWriteExt};
  4. use fxhash::FxHashMap;
  5. use log::{debug, info, warn};
  6. use rand::{rngs::OsRng, RngCore};
  7. use darkfi::{Error, Result};
  8. use crate::{crypto::encrypt_message, privmsg::Privmsg, ChannelInfo, SeenMsgIds};
  9. const RPL_NOTOPIC: u32 = 331;
  10. const RPL_TOPIC: u32 = 332;
  11. pub struct IrcServerConnection {
  12. write_stream: WriteHalf<TcpStream>,
  13. is_nick_init: bool,
  14. is_user_init: bool,
  15. is_registered: bool,
  16. nickname: String,
  17. seen_msg_id: SeenMsgIds,
  18. p2p_sender: async_channel::Sender<Privmsg>,
  19. auto_channels: Vec<String>,
  20. pub configured_chans: FxHashMap<String, ChannelInfo>,
  21. }
  22. impl IrcServerConnection {
  23. pub fn new(
  24. write_stream: WriteHalf<TcpStream>,
  25. seen_msg_id: SeenMsgIds,
  26. p2p_sender: async_channel::Sender<Privmsg>,
  27. auto_channels: Vec<String>,
  28. configured_chans: FxHashMap<String, ChannelInfo>,
  29. ) -> Self {
  30. Self {
  31. write_stream,
  32. is_nick_init: false,
  33. is_user_init: false,
  34. is_registered: false,
  35. nickname: "anon".to_string(),
  36. seen_msg_id,
  37. p2p_sender,
  38. auto_channels,
  39. configured_chans,
  40. }
  41. }
  42. pub async fn update(&mut self, line: String) -> Result<()> {
  43. let mut tokens = line.split_ascii_whitespace();
  44. // Commands can begin with :garbage but we will reject clients doing
  45. // that for now to keep the protocol simple and focused.
  46. let command = tokens.next().ok_or(Error::MalformedPacket)?;
  47. info!("Received command: {}", command.to_uppercase());
  48. match command.to_uppercase().as_str() {
  49. "USER" => {
  50. // We can stuff any extra things like public keys in here.
  51. // Ignore it for now.
  52. self.is_user_init = true;
  53. }
  54. "NICK" => {
  55. let nickname = tokens.next().ok_or(Error::MalformedPacket)?;
  56. self.is_nick_init = true;
  57. let old_nick = std::mem::replace(&mut self.nickname, nickname.to_string());
  58. let nick_reply = format!(":{}!anon@dark.fi NICK {}\r\n", old_nick, self.nickname);
  59. self.reply(&nick_reply).await?;
  60. }
  61. "JOIN" => {
  62. let channels = tokens.next().ok_or(Error::MalformedPacket)?;
  63. for chan in channels.split(',') {
  64. let join_reply = format!(":{}!anon@dark.fi JOIN {}\r\n", self.nickname, chan);
  65. self.reply(&join_reply).await?;
  66. if !self.configured_chans.contains_key(chan) {
  67. self.configured_chans.insert(chan.to_string(), ChannelInfo::new()?);
  68. } else {
  69. let chan_info = self.configured_chans.get_mut(chan).unwrap();
  70. chan_info.joined = true;
  71. }
  72. }
  73. }
  74. "PART" => {
  75. let channels = tokens.next().ok_or(Error::MalformedPacket)?;
  76. for chan in channels.split(',') {
  77. let part_reply = format!(":{}!anon@dark.fi PART {}\r\n", self.nickname, chan);
  78. self.reply(&part_reply).await?;
  79. let chan_info = self.configured_chans.get_mut(chan).unwrap();
  80. chan_info.joined = false;
  81. }
  82. }
  83. "TOPIC" => {
  84. let channel = tokens.next().ok_or(Error::MalformedPacket)?;
  85. if let Some(substr_idx) = line.find(':') {
  86. // Client is setting the topic
  87. if substr_idx >= line.len() {
  88. return Err(Error::MalformedPacket)
  89. }
  90. let topic = &line[substr_idx + 1..];
  91. let chan_info = self.configured_chans.get_mut(channel).unwrap();
  92. chan_info.topic = Some(topic.to_string());
  93. let topic_reply =
  94. format!(":{}!anon@dark.fi TOPIC {} :{}\r\n", self.nickname, channel, topic);
  95. self.reply(&topic_reply).await?;
  96. } else {
  97. // Client is asking or the topic
  98. let chan_info = self.configured_chans.get(channel).unwrap();
  99. let topic_reply = if let Some(topic) = &chan_info.topic {
  100. format!("{} {} {} :{}\r\n", RPL_TOPIC, self.nickname, channel, topic)
  101. } else {
  102. const TOPIC: &str = "No topic is set";
  103. format!("{} {} {} :{}\r\n", RPL_NOTOPIC, self.nickname, channel, TOPIC)
  104. };
  105. self.reply(&topic_reply).await?;
  106. }
  107. }
  108. "PING" => {
  109. let line_clone = line.clone();
  110. let split_line: Vec<&str> = line_clone.split_whitespace().collect();
  111. if split_line.len() > 1 && split_line[0] == "PING" {
  112. let pong = format!("PONG {}\r\n", split_line[1]);
  113. self.reply(&pong).await?;
  114. }
  115. }
  116. "PRIVMSG" => {
  117. let channel = tokens.next().ok_or(Error::MalformedPacket)?;
  118. let substr_idx = line.find(':').ok_or(Error::MalformedPacket)?;
  119. if substr_idx >= line.len() {
  120. return Err(Error::MalformedPacket)
  121. }
  122. let message = &line[substr_idx + 1..];
  123. info!("(Plain) PRIVMSG {} :{}", channel, message);
  124. if self.configured_chans.contains_key(channel) {
  125. let channel_info = self.configured_chans.get(channel).unwrap();
  126. if channel_info.joined {
  127. let message = if let Some(salt_box) = &channel_info.salt_box {
  128. let encrypted = encrypt_message(salt_box, message);
  129. info!("(Encrypted) PRIVMSG {} :{}", channel, encrypted);
  130. encrypted
  131. } else {
  132. message.to_string()
  133. };
  134. let random_id = OsRng.next_u32();
  135. let protocol_msg = Privmsg {
  136. id: random_id,
  137. nickname: self.nickname.clone(),
  138. channel: channel.to_string(),
  139. message,
  140. };
  141. let mut smi = self.seen_msg_id.lock().await;
  142. smi.push(random_id);
  143. drop(smi);
  144. debug!(target: "ircd", "PRIVMSG to be sent: {:?}", protocol_msg);
  145. self.p2p_sender.send(protocol_msg).await?;
  146. }
  147. }
  148. }
  149. "QUIT" => {
  150. // Close the connection
  151. return Err(Error::ServiceStopped)
  152. }
  153. // Below, we implement custom server commands that do not conform
  154. // to the IRC specification. These are specific to our implementation.
  155. "MSGHIST" => {
  156. // Fetch the message history for a certain channel with optional
  157. // max limit.
  158. // MSGHIST #channel num_msgs
  159. let channel = tokens.next().ok_or(Error::MalformedPacket)?;
  160. let num_msgs = if let Some(n) = tokens.next() { i64::from_str(n)? } else { -1 };
  161. info!("Fetching last {} messages for {}", num_msgs, channel);
  162. if num_msgs < 0 {
  163. // Fetch all messages for the channel
  164. } else {
  165. // Fetch newest num_msgs for the channel
  166. }
  167. }
  168. _ => {
  169. warn!("Unimplemented `{}` command", command);
  170. }
  171. }
  172. if !self.is_registered && self.is_nick_init && self.is_user_init {
  173. debug!("Initializing peer connection");
  174. let register_reply = format!(":darkfi 001 {} :Let there be dark\r\n", self.nickname);
  175. self.reply(&register_reply).await?;
  176. self.is_registered = true;
  177. // Auto-joins
  178. macro_rules! autojoin {
  179. ($channel:expr,$topic:expr) => {
  180. let j = format!(":{}!anon@dark.fi JOIN {}\r\n", self.nickname, $channel);
  181. let t = format!(":DarkFi TOPIC {} :{}\r\n", $channel, $topic);
  182. self.reply(&j).await?;
  183. self.reply(&t).await?;
  184. };
  185. }
  186. for chan in self.auto_channels.clone() {
  187. if self.configured_chans.contains_key(&chan) {
  188. let chan_info = self.configured_chans.get_mut(&chan).unwrap();
  189. let topic = if let Some(topic) = chan_info.topic.clone() {
  190. topic
  191. } else {
  192. "n/a".to_string()
  193. };
  194. chan_info.topic = Some(topic.to_string());
  195. autojoin!(chan, topic);
  196. } else {
  197. let mut chan_info = ChannelInfo::new()?;
  198. chan_info.topic = Some("n/a".to_string());
  199. self.configured_chans.insert(chan.clone(), chan_info);
  200. autojoin!(chan, "n/a");
  201. }
  202. }
  203. }
  204. Ok(())
  205. }
  206. pub async fn reply(&mut self, message: &str) -> Result<()> {
  207. self.write_stream.write_all(message.as_bytes()).await?;
  208. debug!("Sent {}", message);
  209. Ok(())
  210. }
  211. }