use std::collections::VecDeque;
use std::io;
use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
use crate::cipher::{Decryptor, Encryptor};
use crate::http::{parse_head, Request, Response};
const MAX_HEAD: usize = 16 * 1024;
const MAX_BODY: usize = 8 * 1024 * 1024;
const READ_CHUNK: usize = 8 * 1024;
pub struct ControlConnection<R, W> {
reader: R,
writer: W,
enc: Option<Encryptor>,
dec: Option<Decryptor>,
plain: VecDeque<u8>,
cipher_pending: Vec<u8>,
}
impl<R, W> ControlConnection<R, W>
where
R: AsyncRead + Unpin,
W: AsyncWrite + Unpin,
{
pub fn new(reader: R, writer: W) -> ControlConnection<R, W> {
ControlConnection {
reader,
writer,
enc: None,
dec: None,
plain: VecDeque::new(),
cipher_pending: Vec::new(),
}
}
pub fn enable_encryption(&mut self, enc: Encryptor, dec: Decryptor) {
self.enc = Some(enc);
self.dec = Some(dec);
}
pub fn is_encrypted(&self) -> bool {
self.enc.is_some()
}
pub async fn read_request(&mut self) -> io::Result<Option<Request>> {
let mut head = Vec::new();
loop {
match self.read_byte().await? {
None if head.is_empty() => return Ok(None),
None => return Err(eof("EOF inside request head")),
Some(b) => head.push(b),
}
if head.ends_with(b"\r\n\r\n") {
break;
}
if head.len() > MAX_HEAD {
return Err(bad("request head too large"));
}
}
let head_str = std::str::from_utf8(&head[..head.len() - 4])
.map_err(|_| bad("non-UTF8 request head"))?;
let (method, target, protocol, headers) = parse_head(head_str)?;
let len = Request::content_length(&headers)?;
if len > MAX_BODY {
return Err(bad("body too large"));
}
let body = self.read_exact_n(len).await?;
Ok(Some(Request::from_parts(
method, target, protocol, headers, body,
)))
}
pub async fn write_response(&mut self, response: &Response) -> io::Result<()> {
let bytes = response.to_bytes();
let framed = match &mut self.enc {
Some(enc) => enc.encrypt(&bytes),
None => bytes,
};
self.writer.write_all(&framed).await?;
self.writer.flush().await
}
async fn read_byte(&mut self) -> io::Result<Option<u8>> {
loop {
if let Some(b) = self.plain.pop_front() {
return Ok(Some(b));
}
match &self.dec {
None => {
let mut b = [0u8; 1];
let n = self.reader.read(&mut b).await?;
return Ok((n == 1).then_some(b[0]));
}
Some(_) => {
if !self.fill_encrypted().await? {
return Ok(None); }
}
}
}
}
async fn read_exact_n(&mut self, n: usize) -> io::Result<Vec<u8>> {
let mut out = Vec::with_capacity(n);
while out.len() < n {
if let Some(b) = self.plain.pop_front() {
out.push(b);
continue;
}
match &self.dec {
None => {
let mut buf = vec![0u8; n - out.len()];
let r = self.reader.read(&mut buf).await?;
if r == 0 {
return Err(eof("EOF inside request body"));
}
out.extend_from_slice(&buf[..r]);
}
Some(_) => {
if !self.fill_encrypted().await? {
return Err(eof("EOF inside encrypted request body"));
}
}
}
}
Ok(out)
}
async fn fill_encrypted(&mut self) -> io::Result<bool> {
let dec = self.dec.as_mut().expect("encrypted mode");
loop {
let (plaintext, used) = dec
.decrypt_available(&self.cipher_pending)
.ok_or_else(|| bad("channel authentication failed"))?;
if used > 0 {
self.cipher_pending.drain(0..used);
}
if !plaintext.is_empty() {
self.plain.extend(plaintext);
return Ok(true);
}
let mut buf = [0u8; READ_CHUNK];
let n = self.reader.read(&mut buf).await?;
if n == 0 {
return Ok(false);
}
self.cipher_pending.extend_from_slice(&buf[..n]);
}
}
}
fn eof(msg: &'static str) -> io::Error {
io::Error::new(io::ErrorKind::UnexpectedEof, msg)
}
fn bad(msg: &'static str) -> io::Error {
io::Error::new(io::ErrorKind::InvalidData, msg)
}
#[cfg(test)]
mod tests {
use super::*;
use crate::cipher::{control_channel, sender_control_channel};
#[tokio::test]
async fn reads_plaintext_then_switches_to_encrypted() {
let (server_side, sender_side) = tokio::io::duplex(64 * 1024);
let (srd, swr) = tokio::io::split(server_side);
let mut conn = ControlConnection::new(srd, swr);
let (mut sender_rd, mut sender_wr) = tokio::io::split(sender_side);
sender_wr
.write_all(b"GET /info HTTP/1.1\r\nCSeq: 0\r\n\r\n")
.await
.unwrap();
let req = conn.read_request().await.unwrap().unwrap();
assert_eq!((req.method.as_str(), req.target.as_str()), ("GET", "/info"));
conn.write_response(&Response::ok("HTTP/1.1"))
.await
.unwrap();
let mut buf = vec![0u8; 256];
let n = sender_rd.read(&mut buf).await.unwrap();
assert!(String::from_utf8_lossy(&buf[..n]).starts_with("HTTP/1.1 200 OK"));
let secret = [0x55u8; 64];
let (enc, dec) = control_channel(&secret);
conn.enable_encryption(enc, dec);
let (mut sender_enc, mut sender_dec) = sender_control_channel(&secret);
let framed =
sender_enc.encrypt(b"POST /x RTSP/1.0\r\nCSeq: 1\r\nContent-Length: 3\r\n\r\nabc");
sender_wr.write_all(&framed).await.unwrap();
let req = conn.read_request().await.unwrap().unwrap();
assert_eq!(req.method, "POST");
assert_eq!(req.body, b"abc");
conn.write_response(&Response::new("RTSP/1.0", 200, "OK"))
.await
.unwrap();
let mut buf = vec![0u8; 256];
let n = sender_rd.read(&mut buf).await.unwrap();
let (plain, _) = sender_dec.decrypt_available(&buf[..n]).unwrap();
assert!(String::from_utf8_lossy(&plain).starts_with("RTSP/1.0 200 OK"));
}
}