use std::io::{Read, Write};
use std::net::TcpStream;
use ferogram_crypto::ObfuscatedCipher;
pub trait Transport {
type Error: std::error::Error + Send + Sync + 'static;
fn send(&mut self, data: &[u8]) -> Result<(), Self::Error>;
fn recv(&mut self) -> Result<Vec<u8>, Self::Error>;
}
pub trait Tagged {
fn init_tag(&mut self) -> [u8; 4];
}
pub struct AbridgedTransport<T: Transport> {
inner: T,
init_sent: bool,
}
impl<T: Transport> AbridgedTransport<T> {
pub fn new(inner: T) -> Self {
Self {
inner,
init_sent: false,
}
}
pub fn send_message(&mut self, data: &[u8]) -> Result<(), T::Error> {
if !self.init_sent {
self.inner.send(&[0xef])?;
self.init_sent = true;
}
let len = data.len() / 4;
let header: Vec<u8> = if len < 127 {
vec![len as u8]
} else {
vec![
0x7f,
(len & 0xff) as u8,
((len >> 8) & 0xff) as u8,
((len >> 16) & 0xff) as u8,
]
};
self.inner.send(&header)?;
self.inner.send(data)
}
pub fn recv_message(&mut self) -> Result<Vec<u8>, T::Error> {
self.inner.recv()
}
pub fn inner_mut(&mut self) -> &mut T {
&mut self.inner
}
}
impl<T: Transport> Tagged for AbridgedTransport<T> {
fn init_tag(&mut self) -> [u8; 4] {
self.init_sent = true; [0xef, 0xef, 0xef, 0xef]
}
}
const FORBIDDEN: &[[u8; 4]] = &[
[b'H', b'E', b'A', b'D'],
[b'P', b'O', b'S', b'T'],
[b'G', b'E', b'T', b' '],
[b'O', b'P', b'T', b'I'],
[0x16, 0x03, 0x01, 0x02],
[0xdd, 0xdd, 0xdd, 0xdd],
[0xee, 0xee, 0xee, 0xee],
];
pub struct ObfuscatedAbridged {
stream: TcpStream,
cipher: ObfuscatedCipher,
header: Option<[u8; 64]>,
}
impl ObfuscatedAbridged {
pub fn new(stream: TcpStream, dc_id: i16) -> std::io::Result<Self> {
let mut init = [0u8; 64];
loop {
getrandom::getrandom(&mut init).expect("getrandom");
if init[0] == 0xef {
continue;
}
if init[4..8] == [0u8; 4] {
continue;
}
if FORBIDDEN.iter().any(|f| f == &init[..4]) {
continue;
}
break;
}
init[56..60].copy_from_slice(&[0xef, 0xef, 0xef, 0xef]);
let dc_bytes = dc_id.to_le_bytes();
init[60] = dc_bytes[0];
init[61] = dc_bytes[1];
let mut cipher = ObfuscatedCipher::new(&init);
let mut enc = init.to_vec();
cipher.encrypt(&mut enc);
init[56..64].copy_from_slice(&enc[56..64]);
Ok(Self {
stream,
cipher,
header: Some(init),
})
}
pub fn send_message(&mut self, data: &[u8]) -> std::io::Result<()> {
if let Some(hdr) = self.header.take() {
self.stream.write_all(&hdr)?;
}
let len = data.len() / 4;
let mut frame: Vec<u8> = if len < 127 {
vec![len as u8]
} else {
vec![
0x7f,
(len & 0xff) as u8,
((len >> 8) & 0xff) as u8,
((len >> 16) & 0xff) as u8,
]
};
frame.extend_from_slice(data);
self.cipher.encrypt(&mut frame);
self.stream.write_all(&frame)
}
pub fn recv_message(&mut self) -> std::io::Result<Vec<u8>> {
let mut first = [0u8; 1];
self.stream.read_exact(&mut first)?;
self.cipher.decrypt(&mut first);
let words = if first[0] < 0x7f {
first[0] as usize
} else if first[0] == 0x7f {
let mut rest = [0u8; 3];
self.stream.read_exact(&mut rest)?;
self.cipher.decrypt(&mut rest);
rest[0] as usize | (rest[1] as usize) << 8 | (rest[2] as usize) << 16
} else {
let mut rest = [0u8; 3];
self.stream.read_exact(&mut rest)?;
self.cipher.decrypt(&mut rest);
let code = i32::from_le_bytes([first[0], rest[0], rest[1], rest[2]]);
return Err(std::io::Error::new(
std::io::ErrorKind::ConnectionRefused,
format!("transport error from server: {code}"),
));
};
let mut payload = vec![0u8; words * 4];
self.stream.read_exact(&mut payload)?;
self.cipher.decrypt(&mut payload);
Ok(payload)
}
}