1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
use std::net::Ipv4Addr;
use std::num::Wrapping;

use common::bytes::BytesAbleNum;

const USERNAME_MAX_LEN: usize = 30;
const MAC_ADDRESS_LEN: usize = 18;

#[derive(Debug)]
pub enum MACOpenErr {
    UsernameTooLong(String),
    MACAddressError(String),
}

#[derive(Debug)]
pub struct MACOpenPacket {
    username: String,
    ipaddress: Ipv4Addr,
    mac_address: String,
    isp: ISPCode,
}

#[derive(Debug)]
pub enum Configuration {
    GUET,
    GXNU,
}

#[derive(Debug, Clone, Copy)]
pub enum ISPCode {
    CChinaUnicom = 1 << 8,
    CChinaTelecom = 2 << 8,
    CChinaMobile = 3 << 8,
}

impl Configuration {
    pub fn hash_key(&self) -> u32 {
        match *self {
            _ => 0x4E67_C6A7,
        }
    }
}

impl MACOpenPacket {
    pub fn new(username: &str, ipaddress: Ipv4Addr, mac_address: &str, isp: ISPCode) -> Self {
        MACOpenPacket {
            username: username.to_string(),
            ipaddress,
            mac_address: mac_address.to_string(),
            isp,
        }
    }

    pub fn as_bytes(&self, hash_key: u32) -> Result<Vec<u8>, MACOpenErr> {
        let mut macopen_packet = Vec::with_capacity(60);
        {
            self.validate()?;

            let mut username_bytes = [0; USERNAME_MAX_LEN];
            let mut mac_address_bytes = [0; MAC_ADDRESS_LEN];
            username_bytes[..self.username.len()].clone_from_slice(self.username.as_bytes());
            mac_address_bytes[..self.mac_address.len()]
                .clone_from_slice(self.mac_address.as_bytes());

            macopen_packet.extend_from_slice(&username_bytes);
            macopen_packet.extend_from_slice(&self.ipaddress.octets());
            macopen_packet.extend_from_slice(&mac_address_bytes);
            macopen_packet.extend((self.isp as u32).as_bytes_be());

            let hash_bytes = Self::hash_bytes(&macopen_packet, hash_key);
            macopen_packet.extend_from_slice(&hash_bytes);
        }

        Ok(macopen_packet)
    }

    fn validate(&self) -> Result<(), MACOpenErr> {
        if self.username.len() > USERNAME_MAX_LEN - 1 {
            return Err(MACOpenErr::UsernameTooLong(self.username.clone()));
        }
        if self.mac_address.len() != MAC_ADDRESS_LEN - 1 {
            return Err(MACOpenErr::MACAddressError(self.mac_address.clone()));
        }
        Ok(())
    }

    fn hash_bytes(bytes: &[u8], hash_key: u32) -> [u8; 4] {
        let mut hash = Wrapping(hash_key as i32);
        for c in bytes.iter() {
            hash ^= (hash << 5) + (hash >> 2) + Wrapping(i32::from(*c));
        }
        hash &= Wrapping(0x7fff_ffff);

        let mut hash_bytes = [0; 4];
        (hash.0 as u32).write_bytes_le(&mut hash_bytes);
        hash_bytes
    }
}


#[test]
fn test_mac_opener_hash_bytes() {
    let bytes1 = [1, 2, 3, 4, 5, 6, 7, 0];
    let hash_bytes1 = MACOpenPacket::hash_bytes(&bytes1, Configuration::GUET.hash_key());

    let bytes2 = [97, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
        0, 0, 0, 0, 172, 16, 1, 1, 52, 48, 58, 54, 49, 58, 56, 54, 58, 56, 55, 58, 57,
        70, 58, 70, 49, 0, 0, 0, 1, 0];
    let hash_bytes2 = MACOpenPacket::hash_bytes(&bytes2, Configuration::GUET.hash_key());

    assert_eq!(hash_bytes1, [0x9c, 0x89, 0xf8, 0x3d]);
    assert_eq!(hash_bytes2, [255, 189, 40, 90]);
}