1use std::io::{self, Read, Write};
12use std::net::SocketAddrV4;
13use std::sync::Arc;
14use std::time::Duration;
15
16use rsa::{RsaPrivateKey, RsaPublicKey};
17use blowfish::Blowfish;
18
19use crate::net::filter::{RsaWriter, RsaReader, BlowfishWriter, BlowfishReader};
20use crate::util::io::*;
21
22use super::{Element, SimpleElement, TopElement, ElementLength};
23
24
25pub mod id {
28 pub const LOGIN_REQUEST: u8 = 0x00;
29 pub const PING: u8 = 0x02;
30 pub const CHALLENGE_RESPONSE: u8 = 0x03;
31}
32
33
34#[derive(Debug, Clone, Copy)]
37pub struct Ping {
38 pub num: u8,
41}
42
43impl SimpleElement for Ping {
44
45 fn encode(&self, write: &mut impl Write) -> io::Result<()> {
46 write.write_u8(self.num)
47 }
48
49 fn decode(read: &mut impl Read, _len: usize) -> io::Result<Self> {
50 Ok(Self { num: read.read_u8()? })
51 }
52
53}
54
55impl TopElement for Ping {
56 const LEN: ElementLength = ElementLength::Fixed(1);
57}
58
59
60#[derive(Debug, Default, Clone)]
63pub struct LoginRequest {
64 pub protocol: u32,
65 pub username: String,
66 pub password: String,
67 pub blowfish_key: Vec<u8>,
68 pub context: String,
69 pub digest: Option<[u8; 16]>,
70 pub nonce: u32,
71}
72
73
74#[derive(Debug, Clone)]
77pub enum LoginResponse {
78 Success(LoginSuccess),
80 Error(LoginError, String),
82 Challenge(LoginChallenge),
84 Unknown(u8),
86}
87
88#[derive(Debug, Clone)]
92pub struct LoginSuccess {
93 pub addr: SocketAddrV4,
96 pub login_key: u32,
98 pub server_message: String,
100}
101
102#[derive(Debug, Clone)]
104pub enum LoginChallenge {
105 CuckooCycle {
107 prefix: String,
108 max_nonce: u64,
109 },
110}
111
112#[derive(Debug, Clone, Copy, PartialEq, Eq)]
114#[repr(u8)]
115pub enum LoginError {
116 MalformedRequest = 64,
117 BadProtocolVersion = 65,
118 InvalidUser = 67,
120 InvalidPassword = 68,
121 AlreadyLoggedIn = 69,
122 BadDigest = 70,
123 DatabaseGeneralFailure = 71,
124 DatabaseNotReady = 72,
125 IllegalCharacters = 73,
126 ServerNotReady = 74,
127 UpdaterNotReady = 75, NoBaseApp = 76,
129 BaseAppOverload = 77,
130 CellAppOverload = 78,
131 BaseAppTimeout = 79,
132 BaseAppManagerTimeout = 80,
133 DatabaseAppOverload = 81,
134 LoginNotAllowed = 82,
135 RateLimited = 83,
136 Banned = 84,
137 ChallengeError = 85,
138}
139
140
141#[derive(Debug, Clone)]
143pub struct ChallengeResponse<T> {
144 pub duration: Duration,
146 pub data: T,
148}
149
150#[derive(Debug, Clone)]
152pub struct CuckooCycleResponse {
153 pub key: String,
154 pub solution: Vec<u32>,
155}
156
157
158#[derive(Debug)]
162pub enum LoginRequestEncryption {
163 Clear,
165 Client(Arc<RsaPublicKey>),
167 Server(Arc<RsaPrivateKey>),
169}
170
171impl Element for LoginRequest {
172
173 type Config = LoginRequestEncryption;
174
175 fn encode(&self, write: &mut impl Write, config: &Self::Config) -> io::Result<()> {
176 write.write_u32(self.protocol)?;
177 match config {
178 LoginRequestEncryption::Clear => {
179 write.write_u8(0)?;
180 encode_login_params(write, self)
181 }
182 LoginRequestEncryption::Client(key) => {
183 write.write_u8(1)?;
184 encode_login_params(RsaWriter::new(write, &key), self)
185 }
186 LoginRequestEncryption::Server(_) => panic!("cannot encode with server login codec"),
187 }
188 }
189
190 fn decode(read: &mut impl Read, _len: usize, config: &Self::Config) -> io::Result<Self> {
191 let protocol = read.read_u32()?;
192 if read.read_u8()? != 0 {
193 if let LoginRequestEncryption::Server(key) = config {
194 decode_login_params(RsaReader::new(read, &key), protocol)
195 } else {
196 Err(io::Error::new(io::ErrorKind::InvalidData, "cannot decode without server login codec"))
197 }
198 } else {
199 decode_login_params(read, protocol)
200 }
201 }
202
203}
204
205impl TopElement for LoginRequest {
206 const LEN: ElementLength = ElementLength::Variable16;
207}
208
209fn encode_login_params(mut write: impl Write, input: &LoginRequest) -> io::Result<()> {
210 write.write_u8(if input.digest.is_some() { 0x01 } else { 0x00 })?;
211 write.write_string_variable(&input.username)?;
212 write.write_string_variable(&input.password)?;
213 write.write_blob_variable(&input.blowfish_key)?;
214 write.write_string_variable(&input.context)?;
215 if let Some(digest) = input.digest {
216 write.write_all(&digest)?;
217 }
218 write.write_u32(input.nonce)
219}
220
221fn decode_login_params(mut input: impl Read, protocol: u32) -> io::Result<LoginRequest> {
222 let flags = input.read_u8()?;
223 Ok(LoginRequest {
224 protocol,
225 username: input.read_string_variable()?,
226 password: input.read_string_variable()?,
227 blowfish_key: input.read_blob_variable()?,
228 context: input.read_string_variable()?,
229 digest: if flags & 0x01 != 0 {
230 let mut digest = [0; 16];
231 input.read_exact(&mut digest)?;
232 Some(digest)
233 } else {
234 Option::None
235 },
236 nonce: input.read_u32()?
237 })
238}
239
240
241#[derive(Debug)]
244pub enum LoginResponseEncryption {
245 Clear,
248 Encrypted(Arc<Blowfish>),
255}
256
257const CHALLENGE_CUCKOO_CYCLE: &'static str = "cuckoo_cycle";
259
260impl Element for LoginResponse {
261
262 type Config = LoginResponseEncryption;
263
264 fn encode(&self, write: &mut impl Write, config: &Self::Config) -> io::Result<()> {
265
266 match self {
267 Self::Success(success) => {
268
269 write.write_u8(1)?; if let LoginResponseEncryption::Encrypted(bf) = config {
272 encode_login_success(BlowfishWriter::new(write, &bf), success)?;
273 } else {
274 encode_login_success(write, success)?;
275 }
276
277 }
278 Self::Error(err, message) => {
279 write.write_u8(*err as _)?;
280 write.write_string_variable(&message)?;
281 }
282 Self::Challenge(challenge) => {
283
284 write.write_u8(66)?;
285
286 match challenge {
287 LoginChallenge::CuckooCycle { prefix, max_nonce } => {
288 write.write_string_variable(CHALLENGE_CUCKOO_CYCLE)?;
289 write.write_string_variable(&prefix)?;
290 write.write_u64(*max_nonce)?;
291 }
292 }
293
294 }
295 Self::Unknown(code) => write.write_u8(*code)?
296 }
297
298 Ok(())
299
300 }
301
302 fn decode(read: &mut impl Read, _len: usize, config: &Self::Config) -> io::Result<Self> {
303
304 let error = match read.read_u8()? {
305 1 => {
306
307 let success =
308 if let LoginResponseEncryption::Encrypted(bf) = config {
309 decode_login_success(BlowfishReader::new(read, &bf))?
310 } else {
311 decode_login_success(read)?
312 };
313
314 return Ok(LoginResponse::Success(success));
315
316 }
317 66 => {
318
319 let challenge_name = read.read_string_variable()?;
320 let challenge = match &challenge_name[..] {
321 CHALLENGE_CUCKOO_CYCLE => {
322 let prefix = read.read_string_variable()?;
323 let max_nonce = read.read_u64()?;
324 LoginChallenge::CuckooCycle { prefix, max_nonce }
325 }
326 _ => return Err(io::Error::new(io::ErrorKind::InvalidData, "invalid challenge name"))
327 };
328
329 return Ok(LoginResponse::Challenge(challenge));
330
331 }
332 64 => LoginError::MalformedRequest,
333 65 => LoginError::BadProtocolVersion,
334 67 => LoginError::InvalidUser,
335 68 => LoginError::InvalidPassword,
336 code => return Ok(LoginResponse::Unknown(code))
338 };
339
340 let message = match read.read_string_variable() {
341 Ok(msg) => msg,
342 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => String::new(),
343 Err(e) => return Err(e),
344 };
345
346 Ok(LoginResponse::Error(error, message))
347
348 }
349
350}
351
352fn encode_login_success<W: Write>(mut write: W, success: &LoginSuccess) -> io::Result<()> {
355 write.write_sock_addr_v4(success.addr)?;
356 write.write_u32(success.login_key)?;
357 if !success.server_message.is_empty() {
358 write.write_string_variable(&success.server_message)?;
359 }
360 Ok(())
361}
362
363fn decode_login_success<R: Read>(mut read: R) -> io::Result<LoginSuccess> {
366 Ok(LoginSuccess {
367 addr: read.read_sock_addr_v4()?,
368 login_key: read.read_u32()?,
369 server_message: match read.read_string_variable() {
370 Ok(msg) => msg,
371 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => String::new(),
372 Err(e) => return Err(e),
373 },
374 })
375}
376
377
378impl<E: Element> Element for ChallengeResponse<E> {
379
380 type Config = E::Config;
381
382 fn encode(&self, write: &mut impl Write, config: &Self::Config) -> io::Result<()> {
383 write.write_f32(self.duration.as_secs_f32())?;
384 self.data.encode(write, config)
385 }
386
387 fn decode(read: &mut impl Read, len: usize, config: &Self::Config) -> io::Result<Self> {
388 Ok(ChallengeResponse {
389 duration: Duration::from_secs_f32(read.read_f32()?),
390 data: E::decode(read, len - 4, config)?
391 })
392 }
393
394}
395
396impl<E: Element> TopElement for ChallengeResponse<E> {
397 const LEN: ElementLength = ElementLength::Variable16;
398}
399
400impl SimpleElement for CuckooCycleResponse {
401
402 fn encode(&self, write: &mut impl Write) -> io::Result<()> {
403 write.write_string_variable(&self.key)?;
404 for &nonce in &self.solution {
405 write.write_u32(nonce)?;
406 }
407 Ok(())
408 }
409
410 fn decode(read: &mut impl Read, _len: usize) -> io::Result<Self> {
411
412 let key = read.read_string_variable()?;
413 let mut solution = Vec::with_capacity(42);
414
415 loop {
416 solution.push(match read.read_u32() {
417 Ok(n) => n,
418 Err(e) if e.kind() == io::ErrorKind::UnexpectedEof => break,
419 Err(e) => return Err(e),
420 });
421 }
422
423 Ok(CuckooCycleResponse {
424 key,
425 solution,
426 })
427
428 }
429
430}