tentacli 15.2.0

Framework for building extensible network protocol clients via modular plugins.
use async_trait::async_trait;
use binrw::{BinRead, BinWrite};
use serde::Serialize;
use std::sync::{Arc, Mutex};
use tokio::sync::RwLock;

use crate::client::prelude::*;
use crate::plugins::wow::wotlk::config::Config;
use crate::plugins::wow::wotlk::login::srp::Srp;
use crate::plugins::wow::wotlk::opcodes::Opcode;

#[derive(Packet, BinRead, Serialize, FieldsMetadata)]
#[br(little)]
pub struct Incoming {
    unknown: u8,
    code: u8,
    server_ephemeral: [u8; 32],
    g_len: u8,
    #[br(count = g_len)]
    g: Vec<u8>,
    n_len: u8,
    #[br(count = n_len)]
    n: Vec<u8>,
    salt: [u8; 32],
    version_challenge: [u8; 16],
    unknown2: u8,
}

#[derive(Packet, BinWrite, Serialize, FieldsMetadata)]
#[name(LOGIN_PROOF)]
#[opcode(U8(Opcode::LOGIN_PROOF))]
#[bw(little)]
struct Outgoing {
    public_ephemeral: [u8; 32],
    client_proof: [u8; 20],
    crc_hash: [u8; 20],
    keys_count: u8,
    security_flags: u8,
}

pub struct Handler {
    pub srp: Arc<Mutex<Srp>>,
}

#[async_trait]
impl PacketHandler for Handler {
    async fn handle(
        &mut self,
        packet: &mut Packet,
        _: Arc<RwLock<CtxMap>>,
    ) -> anyhow::Result<Vec<HandlerOutput>> {
        let Incoming {
            n,
            g,
            server_ephemeral,
            salt,
            ..
        } = Incoming::unpack(packet)?;

        let config: Config = ConfigParser::parse_from_file("wow/wotlk/connection.toml")?;
        let connection = config
            .connection
            .ok_or_else(|| anyhow::anyhow!("Missing [connection]"))?;

        let (account, password) = {
            (
                connection.account_name.to_uppercase(),
                connection.password.to_uppercase(),
            )
        };

        let mut srp_client = self
            .srp
            .lock()
            .map_err(|_| anyhow::anyhow!("Mutex poisoned"))?;
        srp_client.init(&n, &g, &server_ephemeral, salt);
        srp_client.calculate_session_key(&account, &password);

        let client_proof: [u8; 20] = srp_client.calculate_proof(&account);
        let crc_hash: [u8; 20] = [
            0xCD, 0xCB, 0xBD, 0x51, 0x88, 0x31, 0x5E, 0x6B, 0x4D, 0x19, 0x44, 0x9D, 0x49, 0x2D,
            0xBC, 0xFA, 0xF1, 0x56, 0xA3, 0x47,
        ];

        Ok(vec![
            HandlerOutput::Messages(vec![Message {
                msg_type: Default::default(),
                text: "SRP secret created".to_string(),
            }]),
            HandlerOutput::Packets(vec![
                Outgoing {
                    public_ephemeral: srp_client.public_ephemeral(),
                    client_proof,
                    crc_hash,
                    keys_count: 0,
                    security_flags: 0,
                }
                .pack()?,
            ]),
        ])
    }
}