Skip to content

Commit

Permalink
cli: allow listening on range of ports
Browse files Browse the repository at this point in the history
  • Loading branch information
connor4312 committed Jul 26, 2024
1 parent a94e2ca commit e4f4e61
Show file tree
Hide file tree
Showing 2 changed files with 61 additions and 37 deletions.
4 changes: 2 additions & 2 deletions cli/src/commands/args.rs
Original file line number Diff line number Diff line change
Expand Up @@ -230,8 +230,8 @@ pub struct CommandShellArgs {
#[clap(long)]
pub on_socket: bool,
/// Listen on a host/port instead of stdin/stdout.
#[clap(long, num_args = 0..=1, default_missing_value = "0")]
pub on_port: Option<u16>,
#[clap(long, num_args = 0..=2, default_missing_value = "0")]
pub on_port: Vec<u16>,
/// Listen on a host/port instead of stdin/stdout.
#[clap[long]]
pub on_host: Option<String>,
Expand Down
94 changes: 59 additions & 35 deletions cli/src/commands/tunnels.rs
Original file line number Diff line number Diff line change
Expand Up @@ -155,43 +155,52 @@ pub async fn command_shell(ctx: CommandContext, args: CommandShellArgs) -> Resul
code_server_args: (&ctx.args).into(),
};

let mut listener: Box<dyn AsyncRWAccepter> = match (args.on_port, &args.on_host, args.on_socket)
{
(_, _, true) => {
let socket = get_socket_name();
let listener = listen_socket_rw_stream(&socket)
.await
.map_err(|e| wrap(e, "error listening on socket"))?;

params
.log
.result(format!("Listening on {}", socket.display()));

Box::new(listener)
}
(Some(_), _, _) | (_, Some(_), _) => {
let addr = SocketAddr::new(
args.on_host
let mut listener: Box<dyn AsyncRWAccepter> =
match (args.on_port.get(0), &args.on_host, args.on_socket) {
(_, _, true) => {
let socket = get_socket_name();
let listener = listen_socket_rw_stream(&socket)
.await
.map_err(|e| wrap(e, "error listening on socket"))?;

params
.log
.result(format!("Listening on {}", socket.display()));

Box::new(listener)
}
(Some(_), _, _) | (_, Some(_), _) => {
let host = args
.on_host
.as_ref()
.map(|h| h.parse().map_err(CodeError::InvalidHostAddress))
.unwrap_or(Ok(IpAddr::V4(Ipv4Addr::LOCALHOST)))?,
args.on_port.unwrap_or_default(),
);
let listener = tokio::net::TcpListener::bind(addr)
.await
.map_err(|e| wrap(e, "error listening on port"))?;

params
.log
.result(format!("Listening on {}", listener.local_addr().unwrap()));

Box::new(listener)
}
_ => {
serve_stream(tokio::io::stdin(), tokio::io::stderr(), params).await;
return Ok(0);
}
};
.unwrap_or(Ok(IpAddr::V4(Ipv4Addr::LOCALHOST)))?;

let lower_port = args.on_port.get(0).map(|p| *p).unwrap_or_default();
let port_no = if let Some(upper) = args.on_port.get(1) {
find_unused_port(&host, lower_port, *upper)
.await
.unwrap_or_default()
} else {
lower_port
};

let addr = SocketAddr::new(host, port_no);
let listener = tokio::net::TcpListener::bind(addr)
.await
.map_err(|e| wrap(e, "error listening on port"))?;

params
.log
.result(format!("Listening on {}", listener.local_addr().unwrap()));

Box::new(listener)
}
_ => {
serve_stream(tokio::io::stdin(), tokio::io::stderr(), params).await;
return Ok(0);
}
};

let mut servers = FuturesUnordered::new();

Expand All @@ -216,6 +225,21 @@ pub async fn command_shell(ctx: CommandContext, args: CommandShellArgs) -> Resul
}
}

async fn find_unused_port(host: &IpAddr, start_port: u16, end_port: u16) -> Option<u16> {
for port in start_port..=end_port {
if is_port_available(host.clone(), port).await {
return Some(port);
}
}
None
}

async fn is_port_available(host: IpAddr, port: u16) -> bool {
tokio::net::TcpListener::bind(SocketAddr::new(host, port))
.await
.is_ok()
}

pub async fn service(
ctx: CommandContext,
service_args: TunnelServiceSubCommands,
Expand Down

0 comments on commit e4f4e61

Please sign in to comment.