1use crate::server::p2p_support::{DeviceSession, Framer, ReadStream, ScannerSession, Stream};
3use anyhow::{bail, ensure, Context, Result};
4use async_trait::async_trait;
5use md5::{Digest, Md5};
6use serde_json::{json, Value};
7use tokio::io::AsyncWriteExt;
8pub const MAX_COMMAND: usize = 64 * 1024;
9pub fn digest(text: &str) -> String {
10 format!("{:x}", Md5::digest(text.as_bytes()))
11}
12pub fn length(b: &[u8]) -> Result<usize> {
13 let n = u32::from_le_bytes(b.try_into()?) as usize;
14 ensure!(
15 (4..=MAX_COMMAND - 4).contains(&n),
16 "invalid Soulseek length"
17 );
18 Ok(n + 4)
19}
20pub struct Reader<'a> {
21 pub bytes: &'a [u8],
22 pub pos: usize,
23}
24impl<'a> Reader<'a> {
25 pub fn new(bytes: &'a [u8]) -> Self {
26 Self { bytes, pos: 0 }
27 }
28 pub fn take(&mut self, n: usize) -> Result<&'a [u8]> {
29 let end = self.pos.checked_add(n).context("length overflow")?;
30 ensure!(end <= self.bytes.len(), "truncated message");
31 let b = &self.bytes[self.pos..end];
32 self.pos = end;
33 Ok(b)
34 }
35 pub fn u32(&mut self) -> Result<u32> {
36 Ok(u32::from_le_bytes(self.take(4)?.try_into()?))
37 }
38 pub fn u64(&mut self) -> Result<u64> {
39 Ok(u64::from_le_bytes(self.take(8)?.try_into()?))
40 }
41 pub fn boolean(&mut self) -> Result<bool> {
42 let b = self.take(1)?[0];
43 ensure!(b <= 1, "invalid boolean");
44 Ok(b == 1)
45 }
46 pub fn string(&mut self) -> Result<String> {
47 let n = self.u32()? as usize;
48 ensure!(n <= 4096, "string too long");
49 let s = std::str::from_utf8(self.take(n)?)?;
50 ensure!(!s.contains('\0'), "NUL string");
51 Ok(s.into())
52 }
53 pub fn end(&self) -> Result<()> {
54 ensure!(self.pos == self.bytes.len(), "unexpected message suffix");
55 Ok(())
56 }
57}
58pub fn string(out: &mut Vec<u8>, s: &str) -> Result<()> {
59 ensure!(s.len() <= 4096 && !s.contains('\0'), "invalid string");
60 out.extend((s.len() as u32).to_le_bytes());
61 out.extend(s.as_bytes());
62 Ok(())
63}
64pub fn packet(code: u32, data: Vec<u8>) -> Result<Vec<u8>> {
65 ensure!(data.len() <= MAX_COMMAND - 8, "packet too long");
66 let mut b = ((data.len() + 4) as u32).to_le_bytes().to_vec();
67 b.extend(code.to_le_bytes());
68 b.extend(data);
69 Ok(b)
70}
71fn text<'a>(v: &'a Value, k: &str) -> Result<&'a str> {
72 let s = v[k].as_str().with_context(|| format!("{k} required"))?;
73 ensure!(
74 !s.is_empty() && s.len() <= 4096 && !s.contains('\0'),
75 "invalid {k}"
76 );
77 Ok(s)
78}
79pub fn request(v: &Value) -> Result<(u32, Vec<u8>)> {
80 let mut b = vec![];
81 let code = match v["type"].as_str().context("type")? {
82 "soulseek_login" => {
83 let u = text(v, "username")?;
84 let p = text(v, "password")?;
85 ensure!(
86 u.len() <= 30 && u.is_ascii() && u.bytes().all(|c| c >= 32) && u.trim() == u,
87 "invalid username"
88 );
89 string(&mut b, u)?;
90 string(&mut b, p)?;
91 b.extend(177u32.to_le_bytes());
92 string(&mut b, &digest(&format!("{u}{p}")))?;
93 b.extend(1u32.to_le_bytes());
94 1
95 }
96 "soulseek_rooms" => 64,
97 "soulseek_join" => {
98 string(&mut b, text(v, "room")?)?;
99 b.extend(0u32.to_le_bytes());
100 14
101 }
102 "soulseek_leave" => {
103 string(&mut b, text(v, "room")?)?;
104 15
105 }
106 "soulseek_chat" => {
107 string(&mut b, text(v, "room")?)?;
108 string(&mut b, text(v, "message")?)?;
109 13
110 }
111 "soulseek_status" => {
112 string(&mut b, text(v, "username")?)?;
113 7
114 }
115 "soulseek_address" => {
116 string(&mut b, text(v, "username")?)?;
117 3
118 }
119 "soulseek_search" => {
120 b.extend(
121 crate::server::p2p_support::number(v, "ticket", u32::MAX as u64)?.to_le_bytes()
122 [..4]
123 .iter(),
124 );
125 string(&mut b, text(v, "query")?)?;
126 26
127 }
128 _ => bail!("unknown Soulseek action"),
129 };
130 Ok((code, b))
131}
132pub fn validate(v: &Value) -> Result<()> {
133 request(v)?;
134 Ok(())
135}
136#[derive(Default)]
137pub struct Device {
138 frame: Framer,
139 username: Option<String>,
140 pending: Option<(String, String)>,
141}
142#[async_trait]
143impl DeviceSession for Device {
144 async fn read(&mut self, s: &mut ReadStream) -> Result<Vec<u8>> {
145 self.frame.sized(s, 4, MAX_COMMAND, length).await
146 }
147 fn receive(&mut self, b: &[u8]) -> Result<(Vec<u8>, Option<Value>)> {
148 let mut r = Reader::new(&b[4..]);
149 let code = r.u32()?;
150 let mut v = json!({"code":code});
151 if code == 1 {
152 ensure!(
153 self.username.is_none() && self.pending.is_none(),
154 "duplicate login"
155 );
156 let u = r.string()?;
157 let p = r.string()?;
158 let version = r.u32()?;
159 let hash = r.string()?;
160 let minor = r.u32()?;
161 ensure!(hash == digest(&format!("{u}{p}")), "login hash mismatch");
162 validate(&json!({"type":"soulseek_login","username":u,"password":p}))?;
163 v = json!({"code":1,"operation":"login","username":u,"password":p,"client_version":version,"minor_version":minor});
164 self.pending = Some((u, digest(&p)));
165 } else {
166 ensure!(self.username.is_some(), "login required");
167 match code {
168 2 => {
169 let _port = r.u32()?;
170 if r.pos < r.bytes.len() {
171 r.u32()?;
172 r.u32()?;
173 }
174 r.end()?;
175 return Ok((vec![], None));
176 }
177 28 => {
178 ensure!(r.u32()? <= 2, "invalid status");
179 r.end()?;
180 return Ok((vec![], None));
181 }
182 32 => {
183 r.end()?;
184 return Ok((packet(32, vec![])?, None));
185 }
186 64 => v["operation"] = json!("rooms"),
187 3 | 7 => {
188 v["operation"] = json!(if code == 3 { "address" } else { "status" });
189 v["username"] = json!(r.string()?);
190 }
191 13 => {
192 v["operation"] = json!("chat");
193 v["room"] = json!(r.string()?);
194 v["message"] = json!(r.string()?);
195 }
196 14 | 15 => {
197 v["operation"] = json!(if code == 14 { "join" } else { "leave" });
198 v["room"] = json!(r.string()?);
199 if code == 14 && r.pos < r.bytes.len() {
200 ensure!(r.u32()? == 0, "private rooms outside scope");
201 }
202 }
203 26 => {
204 v["operation"] = json!("search");
205 v["ticket"] = json!(r.u32()?);
206 v["query"] = json!(r.string()?);
207 }
208 _ => return Ok((vec![], None)),
209 }
210 }
211 r.end()?;
212 Ok((vec![], Some(v)))
213 }
214 fn answer(&mut self, r: &Value, a: Option<&Value>) -> Result<Vec<u8>> {
215 let empty = json!({});
216 let a = a.unwrap_or(&empty);
217 let code = r["code"].as_u64().context("code")? as u32;
218 let mut b = vec![];
219 match code {
220 1 => {
221 let (u, hash) = self.pending.take().context("no pending login")?;
222 let accepted = a["accepted"] == true;
223 b.push(u8::from(accepted));
224 if accepted {
225 string(&mut b, a["greeting"].as_str().unwrap_or("NetGet"))?;
226 b.extend([1, 0, 0, 127]);
227 string(&mut b, &hash)?;
228 b.push(0);
229 self.username = Some(u);
230 } else {
231 string(&mut b, a["error"].as_str().unwrap_or("INVALIDPASS"))?;
232 }
233 }
234 64 => {
235 let rooms = a["rooms"].as_array().cloned().unwrap_or_default();
236 ensure!(rooms.len() <= 128, "too many rooms");
237 b.extend((rooms.len() as u32).to_le_bytes());
238 for room in &rooms {
239 string(&mut b, room.as_str().context("room name")?)?;
240 }
241 b.extend((rooms.len() as u32).to_le_bytes());
242 for _ in &rooms {
243 b.extend(0u32.to_le_bytes());
244 }
245 b.extend([0; 20]);
246 }
247 14 => {
248 string(&mut b, r["room"].as_str().context("room")?)?;
249 b.extend([0; 20]);
250 }
251 15 => string(&mut b, r["room"].as_str().context("room")?)?,
252 13 => {
253 string(&mut b, r["room"].as_str().context("room")?)?;
254 string(&mut b, self.username.as_deref().context("username")?)?;
255 string(&mut b, r["message"].as_str().context("message")?)?;
256 }
257 7 => {
258 string(&mut b, r["username"].as_str().context("username")?)?;
259 let status = a["status"].as_u64().unwrap_or(0);
260 ensure!(status <= 2, "status");
261 b.extend((status as u32).to_le_bytes());
262 b.push(0);
263 }
264 3 => {
265 string(&mut b, r["username"].as_str().context("username")?)?;
266 let ip: std::net::Ipv4Addr = a["ip"].as_str().unwrap_or("0.0.0.0").parse()?;
267 b.extend(ip.octets().iter().rev());
268 let port = a["port"].as_u64().unwrap_or(0);
269 ensure!(port <= 65535, "port");
270 b.extend((port as u32).to_le_bytes());
271 }
272 26 => return Ok(vec![]),
273 _ => bail!("unknown reply"),
274 };
275 packet(code, b)
276 }
277}
278#[derive(Default)]
279pub struct Scanner {
280 frame: Framer,
281 authenticated: bool,
282}
283fn response(b: &[u8]) -> Result<Value> {
284 let mut r = Reader::new(&b[4..]);
285 let code = r.u32()?;
286 let mut v = json!({"code":code});
287 match code {
288 1 => {
289 let success = r.boolean()?;
290 v["success"] = json!(success);
291 if success {
292 v["greeting"] = json!(r.string()?);
293 let ip = r.take(4)?;
294 v["ip"] = json!(std::net::Ipv4Addr::new(ip[3], ip[2], ip[1], ip[0]).to_string());
295 r.string()?;
296 v["privileged"] = json!(r.boolean()?);
297 } else {
298 v["reason"] = json!(r.string()?);
299 }
300 }
301 3 => {
302 v["username"] = json!(r.string()?);
303 let ip = r.take(4)?;
304 v["ip"] = json!(std::net::Ipv4Addr::new(ip[3], ip[2], ip[1], ip[0]).to_string());
305 v["port"] = json!(r.u32()?);
306 if r.pos < r.bytes.len() {
307 r.u32()?;
308 r.take(2)?;
309 }
310 }
311 7 => {
312 v["username"] = json!(r.string()?);
313 let s = r.u32()?;
314 ensure!(s <= 2, "status");
315 v["status"] = json!(s);
316 v["privileged"] = json!(r.boolean()?);
317 }
318 13 => {
319 v["room"] = json!(r.string()?);
320 v["username"] = json!(r.string()?);
321 v["message"] = json!(r.string()?);
322 }
323 15 => v["room"] = json!(r.string()?),
324 64 => {
325 let count = r.u32()? as usize;
326 ensure!(count <= 128, "room count");
327 let mut rooms = vec![];
328 for _ in 0..count {
329 rooms.push(r.string()?);
330 }
331 ensure!(r.u32()? as usize == count, "room count mismatch");
332 let mut counts = vec![];
333 for _ in 0..count {
334 counts.push(r.u32()?);
335 }
336 v["rooms"] = json!(rooms);
337 v["user_counts"] = json!(counts);
338 for _ in 0..5 {
339 ensure!(r.u32()? == 0, "private rooms outside scope");
340 }
341 }
342 14 => {
343 v["room"] = json!(r.string()?);
344 let n = r.u32()?;
345 ensure!(n <= 128, "room users cap");
346 let mut users = vec![];
347 for _ in 0..n {
348 users.push(r.string()?);
349 }
350 let mut status = vec![];
351 ensure!(r.u32()? == n, "user status count");
352 for _ in 0..n {
353 let x = r.u32()?;
354 ensure!(x <= 2, "user status");
355 status.push(x);
356 }
357 ensure!(r.u32()? == n, "statistics count");
358 let mut stats = vec![];
359 for _ in 0..n {
360 stats.push(json!({"speed":r.u32()?,"uploads":r.u64()?,"files":r.u32()?,"folders":r.u32()?}));
361 }
362 ensure!(r.u32()? == n, "slot count");
363 let mut slots = vec![];
364 for _ in 0..n {
365 slots.push(r.u32()?);
366 }
367 ensure!(r.u32()? == n, "country count");
368 let mut countries = vec![];
369 for _ in 0..n {
370 countries.push(r.string()?);
371 }
372 v["users"] = json!(users);
373 v["status"] = json!(status);
374 v["stats"] = json!(stats);
375 v["slots_free"] = json!(slots);
376 v["countries"] = json!(countries);
377 if r.pos < r.bytes.len() {
378 v["owner"] = json!(r.string()?);
379 let n = r.u32()?;
380 ensure!(n <= 128, "operator count");
381 let mut operators = vec![];
382 for _ in 0..n {
383 operators.push(r.string()?);
384 }
385 v["operators"] = json!(operators);
386 }
387 }
388 26 => {
389 v["username"] = json!(r.string()?);
390 v["ticket"] = json!(r.u32()?);
391 v["query"] = json!(r.string()?);
392 }
393 32 => {}
394 _ => {
395 use base64::Engine;
396 v["payload_base64"] =
397 json!(base64::engine::general_purpose::STANDARD
398 .encode(r.take(r.bytes.len() - r.pos)?));
399 }
400 }
401 r.end()?;
402 Ok(v)
403}
404#[async_trait]
405impl ScannerSession for Scanner {
406 async fn open(&mut self, _: &mut Stream) -> Result<()> {
407 Ok(())
408 }
409 async fn exchange(&mut self, s: &mut Stream, a: &Value) -> Result<Value> {
410 let (code, b) = request(a)?;
411 ensure!(code == 1 || self.authenticated, "login required");
412 s.write_all(&packet(code, b)?).await?;
413 if code == 26 {
414 return Ok(json!({"sent":true,"ticket":a["ticket"]}));
415 }
416 let mut announcements = vec![];
417 let mut v = loop {
418 let incoming = response(&self.frame.sized(s, 4, MAX_COMMAND, length).await?)?;
419 if incoming["code"] == code {
420 break incoming;
421 }
422 ensure!(announcements.len() < 32, "announcement cap");
423 announcements.push(incoming);
424 };
425 if !announcements.is_empty() {
426 v["announcements"] = json!(announcements);
427 }
428 if code == 1 {
429 self.authenticated = v["success"] == true;
430 }
431 Ok(v)
432 }
433 async fn idle(&mut self, s: &mut Stream) -> Result<Option<Value>> {
434 Ok(Some(response(
435 &self.frame.sized(s, 4, MAX_COMMAND, length).await?,
436 )?))
437 }
438}