use std::{
net::{IpAddr, SocketAddr},
sync::Arc,
};
use tokio::net::UdpSocket;
use crate::{transport, Error};
#[derive(Clone, Debug)]
pub struct Allocator {
pub relay_address: IpAddr,
pub min_port: u16,
pub max_port: u16,
pub max_retries: u16,
pub address: String,
}
impl Allocator {
pub async fn allocate_conn(
&self,
use_ipv4: bool,
requested_port: u16,
) -> Result<(Arc<UdpSocket>, SocketAddr), Error> {
let max_retries =
if self.max_retries == 0 { 10 } else { self.max_retries };
if requested_port == 0 {
for _ in 0..max_retries {
let port = self.min_port
+ rand::random::<u16>()
% (self.max_port - self.min_port + 1);
let addr = transport::lookup_host(
use_ipv4,
&format!("{}:{port}", self.address),
)
.await?;
let Ok(conn) = UdpSocket::bind(addr).await else {
continue;
};
let mut relay_addr =
conn.local_addr().map_err(transport::Error::from)?;
relay_addr.set_ip(self.relay_address);
return Ok((Arc::new(conn), relay_addr));
}
Err(Error::MaxRetriesExceeded)
} else {
let addr = transport::lookup_host(
use_ipv4,
&format!("{}:{requested_port}", self.address),
)
.await?;
let conn = Arc::new(
UdpSocket::bind(addr).await.map_err(transport::Error::from)?,
);
let mut relay_addr =
conn.local_addr().map_err(transport::Error::from)?;
relay_addr.set_ip(self.relay_address);
Ok((conn, relay_addr))
}
}
}