use super::super::PeerReporter;
use local_async_utils::prelude::*;
use mtorrent_core::pwp;
use mtorrent_utils::peer_id::PeerId;
use std::io;
use std::net::{Ipv4Addr, Ipv6Addr, SocketAddr};
use tokio::net::{TcpSocket, TcpStream};
use tokio::runtime;
use tokio_util::sync::CancellationToken;
macro_rules! marshal_stream {
($stream:expr, $rt_handle:expr) => {{
let std_stream = $stream.into_std()?;
let _g = $rt_handle.enter();
TcpStream::from_std(std_stream)?
}};
}
fn bound_pwp_socket(local_addr: SocketAddr) -> io::Result<TcpSocket> {
let socket = match local_addr {
SocketAddr::V4(_) => TcpSocket::new_v4()?,
SocketAddr::V6(_) => TcpSocket::new_v6()?,
};
socket.set_reuseaddr(true)?;
#[cfg(not(windows))]
socket.set_reuseport(true)?;
#[expect(deprecated)]
socket.set_linger(Some(sec!(0)))?;
socket.set_nodelay(true)?;
socket.bind(local_addr)?;
Ok(socket)
}
pub async fn new_outbound_connection(
local_peer_id: &PeerId,
info_hash: &[u8; 20],
extension_protocol_enabled: bool,
peer_addr: SocketAddr,
local_port: u16,
pwp_runtime: &runtime::Handle,
) -> io::Result<(pwp::DownloadChannels, pwp::UploadChannels, Option<pwp::ExtendedChannels>)> {
let local_addr = match &peer_addr {
SocketAddr::V4(_) => Ipv4Addr::UNSPECIFIED.into(),
SocketAddr::V6(_) => Ipv6Addr::UNSPECIFIED.into(),
};
let socket = bound_pwp_socket(SocketAddr::new(local_addr, local_port))?;
let stream = socket.connect(peer_addr).await?;
let stream = marshal_stream!(stream, pwp_runtime);
let (download_chans, upload_chans, extended_chans, runner) =
pwp::channels_for_outbound_connection(
local_peer_id,
info_hash,
extension_protocol_enabled,
peer_addr,
stream,
None,
)
.await?;
pwp_runtime.spawn(async move {
if let Err(e) = runner.await
&& e.kind() != io::ErrorKind::BrokenPipe
&& e.kind() != io::ErrorKind::UnexpectedEof
{
log::warn!("Peer runner for {peer_addr} exited: {e}");
}
});
Ok((download_chans, upload_chans, extended_chans))
}
pub async fn new_inbound_connection(
local_peer_id: &[u8; 20],
info_hash: &[u8; 20],
extension_protocol_enabled: bool,
remote_ip: SocketAddr,
stream: TcpStream,
pwp_runtime: &runtime::Handle,
) -> io::Result<(pwp::DownloadChannels, pwp::UploadChannels, Option<pwp::ExtendedChannels>)> {
let (download_chans, upload_chans, extended_chans, runner) =
pwp::channels_for_inbound_connection(
local_peer_id,
Some(info_hash),
extension_protocol_enabled,
remote_ip,
stream,
)
.await?;
pwp_runtime.spawn(async move {
if let Err(e) = runner.await
&& e.kind() != io::ErrorKind::BrokenPipe
&& e.kind() != io::ErrorKind::UnexpectedEof
{
log::warn!("Peer runner for {remote_ip} exited: {e}");
}
});
Ok((download_chans, upload_chans, extended_chans))
}
pub async fn run_listener(
local_addr: SocketAddr,
peer_reporter: PeerReporter,
canceller: CancellationToken,
) -> io::Result<()> {
let task = async move {
let socket = bound_pwp_socket(local_addr)?;
let listener = socket.listen(1024)?;
log::info!("TCP listener started on {}", listener.local_addr()?);
loop {
let (stream, addr) = listener.accept().await?;
peer_reporter.report_accepted(addr, stream).await;
}
};
canceller.run_until_cancelled_owned(task).await.unwrap_or(Ok(()))
}