use super::MAX_NONCE_LEN;
use super::handlers_ap1 as handlers;
use super::rtsp;
use super::types::*;
use crate::crypto::fairplay::FairPlay;
use crate::crypto::pairing::Pairing;
use crate::crypto::rsa::RsaKey;
use crate::net::server::{ConnectionHandler, HttpdCallbacks};
use crate::proto::digest;
use crate::proto::http::{HttpRequest, HttpResponse};
use std::net::SocketAddr;
use std::sync::Arc;
pub(crate) struct RaopShared {
pub(crate) rsakey: Arc<RsaKey>,
pub(crate) pairing: Arc<Pairing>,
pub(crate) hwaddr: Vec<u8>,
pub(crate) password: String,
pub(crate) handler: Arc<dyn AudioHandler>,
#[cfg(feature = "ap2")]
pub(crate) pairing_store: Arc<dyn PairingStore>,
pub(crate) output_sample_rate: Option<u32>,
pub(crate) output_max_channels: Option<u8>,
#[cfg(feature = "ap2")]
pub(crate) pin: Option<String>,
#[cfg(feature = "video")]
pub(crate) video_handler: Option<Arc<dyn crate::raop::video::VideoHandler>>,
#[cfg(feature = "video")]
pub(crate) video_ekey: Arc<std::sync::RwLock<Option<[u8; 16]>>>,
#[cfg(feature = "video")]
pub(crate) video_eiv: Arc<std::sync::RwLock<Option<[u8; 16]>>>,
#[cfg(feature = "ap2")]
pub(crate) pairing_id: String,
#[cfg(feature = "ap2")]
pub(crate) airplay_name: String,
#[cfg(feature = "hls")]
pub(crate) hls_handler: Option<Arc<dyn crate::raop::hls::HlsHandler>>,
}
impl HttpdCallbacks for RaopShared {
fn conn_init(&self, local: SocketAddr, remote: SocketAddr) -> Option<Box<dyn ConnectionHandler>> {
let local_bytes = match local.ip() {
std::net::IpAddr::V4(ip) => ip.octets().to_vec(),
std::net::IpAddr::V6(ip) => ip.octets().to_vec(),
};
let remote_bytes = match remote.ip() {
std::net::IpAddr::V4(ip) => ip.octets().to_vec(),
std::net::IpAddr::V6(ip) => ip.octets().to_vec(),
};
let conn = handlers::RaopConnection {
raop_rtp: None,
fairplay: FairPlay::new(),
pairing: self.pairing.create_session(),
local_addr: local_bytes,
remote_addr: remote_bytes,
remote_socket: remote,
nonce: digest::generate_nonce(MAX_NONCE_LEN),
rsakey: self.rsakey.clone(),
pairing_identity: self.pairing.clone(),
hwaddr: self.hwaddr.clone(),
password: self.password.clone(),
handler: self.handler.clone(),
#[cfg(feature = "ap2")]
device_id: crate::util::hwaddr_airplay(&self.hwaddr),
#[cfg(feature = "ap2")]
pairing_id: self.pairing_id.clone(),
#[cfg(feature = "ap2")]
airplay_name: self.airplay_name.clone(),
#[cfg(feature = "ap2")]
srp_server: None,
#[cfg(feature = "ap2")]
pair_verify: None,
#[cfg(feature = "ap2")]
ap2_shared_secret: None,
#[cfg(feature = "ap2")]
pair_verify_secret: None,
#[cfg(feature = "ap2")]
is_ap2: false,
#[cfg(feature = "ap2")]
pairing_store: self.pairing_store.clone(),
#[cfg(feature = "ap2")]
playout_cmd: None,
output_sample_rate: self.output_sample_rate,
output_max_channels: self.output_max_channels,
#[cfg(feature = "ap2")]
pin: self.pin.clone(),
#[cfg(feature = "ap2")]
event_sender: None,
#[cfg(feature = "video")]
video_handler: self.video_handler.clone(),
#[cfg(feature = "video")]
ekey: None,
#[cfg(feature = "video")]
eiv: None,
#[cfg(feature = "video")]
shared_video_ekey: self.video_ekey.clone(),
#[cfg(feature = "video")]
shared_video_eiv: self.video_eiv.clone(),
#[cfg(feature = "hls")]
hls_handler: self.hls_handler.clone(),
#[cfg(feature = "hls")]
hls_state: crate::raop::hls::HlsState::new(),
};
let remote_str = remote.ip().to_string();
conn.handler.on_client_connected(&remote_str);
Some(Box::new(RaopConnectionHandler {
conn,
remote_addr: remote_str,
#[cfg(feature = "ap2")]
cipher: None,
#[cfg(feature = "ap2")]
pending_secret: None,
}))
}
}
struct RaopConnectionHandler {
conn: handlers::RaopConnection,
remote_addr: String,
#[cfg(feature = "ap2")]
cipher: Option<crate::crypto::chacha_transport::EncryptedChannel>,
#[cfg(feature = "ap2")]
pending_secret: Option<Vec<u8>>,
}
impl Drop for RaopConnectionHandler {
fn drop(&mut self) {
self.conn.handler.on_client_disconnected(&self.remote_addr);
}
}
impl ConnectionHandler for RaopConnectionHandler {
fn conn_request(&mut self, request: &HttpRequest) -> HttpResponse {
let resp = rtsp::dispatch(&mut self.conn, request);
#[cfg(feature = "ap2")]
if self.cipher.is_none()
&& let Some(secret) = &self.conn.ap2_shared_secret
{
self.pending_secret = Some(secret.clone());
}
resp
}
fn is_encrypted(&self) -> bool {
#[cfg(feature = "ap2")]
{
self.cipher.is_some()
}
#[cfg(not(feature = "ap2"))]
{
false
}
}
fn after_response(&mut self) {
#[cfg(feature = "ap2")]
if self.cipher.is_none()
&& let Some(secret) = self.pending_secret.take()
{
tracing::debug!(secret_len = secret.len(), "Activating cipher from pending_secret");
match crate::crypto::chacha_transport::EncryptedChannel::control(&secret) {
Ok(ch) => {
tracing::info!("Encrypted RTSP transport activated");
self.cipher = Some(ch);
}
Err(e) => tracing::warn!("Failed to create cipher: {e}"),
}
}
}
fn decrypt_incoming(&mut self, data: &[u8]) -> Option<(Vec<u8>, usize)> {
#[cfg(feature = "ap2")]
if let Some(ch) = &mut self.cipher {
return ch.decrypt_ctx.decrypt(data).ok();
}
Some((data.to_vec(), data.len()))
}
fn encrypt_outgoing(&mut self, data: &[u8]) -> Vec<u8> {
#[cfg(feature = "ap2")]
if let Some(ch) = &mut self.cipher {
return ch.encrypt_ctx.encrypt(data).unwrap_or_else(|_| data.to_vec());
}
data.to_vec()
}
}