use anyhow::Result;
use std::collections::HashMap;
use std::sync::Arc;
use tokio::io::AsyncWriteExt;
use tokio::sync::RwLock;
const RPC_CMD_REGISTER_PEER: u8 = 18; const REGISTER_PEER_PAYLOAD_SIZE: usize = 4 + 128;
pub type PortRewriteMap = Arc<RwLock<HashMap<u16, u16>>>;
pub fn new_rewrite_map() -> PortRewriteMap {
Arc::new(RwLock::new(HashMap::new()))
}
pub async fn relay_with_rewrite(
mut quic_recv: iroh::endpoint::RecvStream,
mut tcp_write: tokio::io::WriteHalf<tokio::net::TcpStream>,
port_map: PortRewriteMap,
) -> Result<()> {
loop {
let mut cmd_buf = [0u8; 1];
if quic_recv.read_exact(&mut cmd_buf).await.is_err() {
break; }
let cmd = cmd_buf[0];
let mut size_buf = [0u8; 8];
quic_recv.read_exact(&mut size_buf).await?;
let payload_size = u64::from_le_bytes(size_buf);
if cmd == RPC_CMD_REGISTER_PEER && payload_size as usize == REGISTER_PEER_PAYLOAD_SIZE {
let mut payload = vec![0u8; payload_size as usize];
quic_recv.read_exact(&mut payload).await?;
let peer_id = u32::from_le_bytes([payload[0], payload[1], payload[2], payload[3]]);
let endpoint_bytes = &payload[4..132];
let endpoint_str = std::str::from_utf8(
&endpoint_bytes[..endpoint_bytes.iter().position(|&b| b == 0).unwrap_or(128)],
)
.unwrap_or("")
.to_string();
if let Some(port_str) = endpoint_str.rsplit(':').next() {
if let Ok(remote_port) = port_str.parse::<u16>() {
let map = port_map.read().await;
if let Some(&local_port) = map.get(&remote_port) {
let new_endpoint = format!("127.0.0.1:{local_port}");
let mut new_endpoint_bytes = [0u8; 128];
let copy_len = new_endpoint.len().min(127);
new_endpoint_bytes[..copy_len]
.copy_from_slice(&new_endpoint.as_bytes()[..copy_len]);
payload[4..132].copy_from_slice(&new_endpoint_bytes);
tracing::info!(
"Rewrote REGISTER_PEER: peer_id={peer_id} \
{endpoint_str} → 127.0.0.1:{local_port}"
);
} else {
tracing::warn!(
"REGISTER_PEER: no rewrite mapping for port {remote_port} \
(peer_id={peer_id}, endpoint={endpoint_str}), passing through"
);
}
}
}
tcp_write.write_all(&[cmd]).await?;
tcp_write.write_all(&size_buf).await?;
tcp_write.write_all(&payload).await?;
} else {
tcp_write.write_all(&[cmd]).await?;
tcp_write.write_all(&size_buf).await?;
let mut remaining = payload_size;
let mut buf = vec![0u8; 64 * 1024];
while remaining > 0 {
let to_read = (remaining as usize).min(buf.len());
let n = quic_recv
.read(&mut buf[..to_read])
.await?
.ok_or_else(|| anyhow::anyhow!("stream closed mid-payload"))?;
tcp_write.write_all(&buf[..n]).await?;
remaining -= n as u64;
}
}
tcp_write.flush().await?;
}
Ok(())
}