server.rs 8.9 KB

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