mirror of
https://github.com/zed-industries/zed.git
synced 2026-05-31 19:05:00 +07:00
dap: Support IPv6 addresses in TCP transport (#52244)
The DAP TCP transport layer was hardcoded to `Ipv4Addr`, so IPv6 addresses like `fd00::a` in a debug config's `connect.host` always failed with `hostname must be IPv4: invalid IPv4 address syntax`. Replaced `Ipv4Addr` with `IpAddr` and `SocketAddrV4` with `SocketAddr` across the `task`, `dap`, `dap_adapters`, and `project` crates. The WASM extension API still uses `u32` for the host field to avoid a breaking WIT interface change; IPv4 round-trips through extensions as before. Fixes #52237 Release Notes: - Fixed DAP TCP transport rejecting IPv6 addresses when connecting to remote debug adapters. --------- Co-authored-by: moktamd <moktamd@users.noreply.github.com>
This commit is contained in:
parent
d7d1b54044
commit
5aa7eaa508
8 changed files with 42 additions and 31 deletions
|
|
@ -18,7 +18,7 @@ use std::{
|
|||
borrow::Borrow,
|
||||
ffi::OsStr,
|
||||
fmt::Debug,
|
||||
net::Ipv4Addr,
|
||||
net::IpAddr,
|
||||
ops::Deref,
|
||||
path::{Path, PathBuf},
|
||||
sync::Arc,
|
||||
|
|
@ -106,7 +106,7 @@ impl<'a> From<&'a str> for DebugAdapterName {
|
|||
|
||||
#[derive(Debug, Clone, PartialEq, Serialize)]
|
||||
pub struct TcpArguments {
|
||||
pub host: Ipv4Addr,
|
||||
pub host: IpAddr,
|
||||
pub port: u16,
|
||||
pub timeout: Option<u64>,
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ pub mod proto_conversions;
|
|||
mod registry;
|
||||
pub mod transport;
|
||||
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::IpAddr;
|
||||
|
||||
pub use dap_types::*;
|
||||
use debugger_settings::DebuggerSettings;
|
||||
|
|
@ -26,7 +26,7 @@ use task::{DebugScenario, TcpArgumentsTemplate};
|
|||
|
||||
pub async fn configure_tcp_connection(
|
||||
tcp_connection: TcpArgumentsTemplate,
|
||||
) -> anyhow::Result<(Ipv4Addr, u16, Option<u64>)> {
|
||||
) -> anyhow::Result<(IpAddr, u16, Option<u64>)> {
|
||||
let host = tcp_connection.host();
|
||||
let timeout = tcp_connection.timeout;
|
||||
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ use smol::{
|
|||
};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
net::{Ipv4Addr, SocketAddrV4},
|
||||
net::{IpAddr, SocketAddr},
|
||||
process::Stdio,
|
||||
sync::Arc,
|
||||
time::Duration,
|
||||
|
|
@ -472,7 +472,7 @@ impl TransportDelegate {
|
|||
pub struct TcpTransport {
|
||||
executor: BackgroundExecutor,
|
||||
pub port: u16,
|
||||
pub host: Ipv4Addr,
|
||||
pub host: IpAddr,
|
||||
pub timeout: u64,
|
||||
process: Arc<Mutex<Option<Child>>>,
|
||||
_stderr_task: Option<Task<()>>,
|
||||
|
|
@ -489,8 +489,8 @@ impl TcpTransport {
|
|||
}
|
||||
}
|
||||
|
||||
pub async fn unused_port(host: Ipv4Addr) -> Result<u16> {
|
||||
Ok(TcpListener::bind(SocketAddrV4::new(host, 0))
|
||||
pub async fn unused_port(host: IpAddr) -> Result<u16> {
|
||||
Ok(TcpListener::bind(SocketAddr::new(host, 0))
|
||||
.await?
|
||||
.local_addr()?
|
||||
.port())
|
||||
|
|
@ -598,7 +598,7 @@ impl Transport for TcpTransport {
|
|||
> {
|
||||
let executor = self.executor.clone();
|
||||
let timeout = self.timeout;
|
||||
let address = SocketAddrV4::new(self.host, self.port);
|
||||
let address = SocketAddr::new(self.host, self.port);
|
||||
let process = self.process.clone();
|
||||
executor.clone().spawn(async move {
|
||||
select! {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use smol::fs::File;
|
|||
use smol::io::AsyncReadExt;
|
||||
use smol::lock::OnceCell;
|
||||
use std::ffi::OsString;
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::IpAddr;
|
||||
use std::str::FromStr;
|
||||
use std::{
|
||||
ffi::OsStr,
|
||||
|
|
@ -42,7 +42,7 @@ impl PythonDebugAdapter {
|
|||
const LANGUAGE_NAME: &'static str = "Python";
|
||||
|
||||
async fn generate_debugpy_arguments<'a>(
|
||||
host: &'a Ipv4Addr,
|
||||
host: &'a IpAddr,
|
||||
port: u16,
|
||||
launch_mode: DebugpyLaunchMode<'a>,
|
||||
user_installed_path: Option<&'a Path>,
|
||||
|
|
@ -380,7 +380,7 @@ impl PythonDebugAdapter {
|
|||
}
|
||||
|
||||
if let Some(hostname) = config_host {
|
||||
tcp_connection.host = Some(hostname.parse().context("hostname must be IPv4")?);
|
||||
tcp_connection.host = Some(hostname.parse().context("invalid IP address")?);
|
||||
}
|
||||
tcp_connection.port = config_port;
|
||||
DebugpyLaunchMode::AttachWithConnect { host: config_host }
|
||||
|
|
@ -974,7 +974,7 @@ mod tests {
|
|||
.contains("Cannot have two different ports")
|
||||
);
|
||||
|
||||
let host = Ipv4Addr::new(127, 0, 0, 1);
|
||||
let host = IpAddr::V4(std::net::Ipv4Addr::LOCALHOST);
|
||||
let config_with_host_conflict = json!({
|
||||
"request": "attach",
|
||||
"connect": {
|
||||
|
|
@ -1018,7 +1018,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_attach_with_connect_mode_generates_correct_arguments() {
|
||||
let host = Ipv4Addr::new(127, 0, 0, 1);
|
||||
let host = IpAddr::V4(std::net::Ipv4Addr::LOCALHOST);
|
||||
let port = 5678;
|
||||
|
||||
let args_without_host = PythonDebugAdapter::generate_debugpy_arguments(
|
||||
|
|
@ -1071,7 +1071,7 @@ mod tests {
|
|||
|
||||
#[gpui::test]
|
||||
async fn test_debugpy_install_path_cases() {
|
||||
let host = Ipv4Addr::new(127, 0, 0, 1);
|
||||
let host = IpAddr::V4(std::net::Ipv4Addr::LOCALHOST);
|
||||
let port = 5678;
|
||||
|
||||
// Case 1: User-defined debugpy path (highest precedence)
|
||||
|
|
|
|||
|
|
@ -24,7 +24,7 @@ use project::project_settings::ProjectSettings;
|
|||
use semver::Version;
|
||||
use std::{
|
||||
env,
|
||||
net::Ipv4Addr,
|
||||
net::{IpAddr, Ipv4Addr},
|
||||
path::{Path, PathBuf},
|
||||
str::FromStr,
|
||||
sync::{Arc, OnceLock},
|
||||
|
|
@ -117,7 +117,7 @@ impl TryFrom<StartDebuggingRequestArguments> for extension::StartDebuggingReques
|
|||
impl From<TcpArguments> for extension::TcpArguments {
|
||||
fn from(value: TcpArguments) -> Self {
|
||||
Self {
|
||||
host: value.host.into(),
|
||||
host: IpAddr::V4(Ipv4Addr::from_bits(value.host)),
|
||||
port: value.port,
|
||||
timeout: value.timeout,
|
||||
}
|
||||
|
|
@ -127,7 +127,10 @@ impl From<TcpArguments> for extension::TcpArguments {
|
|||
impl From<extension::TcpArgumentsTemplate> for TcpArgumentsTemplate {
|
||||
fn from(value: extension::TcpArgumentsTemplate) -> Self {
|
||||
Self {
|
||||
host: value.host.map(Ipv4Addr::to_bits),
|
||||
host: value.host.and_then(|addr| match addr {
|
||||
IpAddr::V4(v4) => Some(v4.to_bits()),
|
||||
IpAddr::V6(_) => None,
|
||||
}),
|
||||
port: value.port,
|
||||
timeout: value.timeout,
|
||||
}
|
||||
|
|
@ -137,7 +140,7 @@ impl From<extension::TcpArgumentsTemplate> for TcpArgumentsTemplate {
|
|||
impl From<TcpArgumentsTemplate> for extension::TcpArgumentsTemplate {
|
||||
fn from(value: TcpArgumentsTemplate) -> Self {
|
||||
Self {
|
||||
host: value.host.map(Ipv4Addr::from_bits),
|
||||
host: value.host.map(|bits| IpAddr::V4(Ipv4Addr::from_bits(bits))),
|
||||
port: value.port,
|
||||
timeout: value.timeout,
|
||||
}
|
||||
|
|
@ -904,13 +907,21 @@ impl dap::Host for WasmState {
|
|||
let (host, port, timeout) =
|
||||
::dap::configure_tcp_connection(task::TcpArgumentsTemplate {
|
||||
port: template.port,
|
||||
host: template.host.map(Ipv4Addr::from_bits),
|
||||
host: template
|
||||
.host
|
||||
.map(|bits| IpAddr::V4(Ipv4Addr::from_bits(bits))),
|
||||
timeout: template.timeout,
|
||||
})
|
||||
.await?;
|
||||
let host_bits = match host {
|
||||
IpAddr::V4(v4) => v4.to_bits(),
|
||||
IpAddr::V6(_) => {
|
||||
anyhow::bail!("IPv6 addresses are not supported in the extension API")
|
||||
}
|
||||
};
|
||||
Ok(TcpArguments {
|
||||
port,
|
||||
host: host.to_bits(),
|
||||
host: host_bits,
|
||||
timeout,
|
||||
})
|
||||
})
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ use std::{
|
|||
borrow::Borrow,
|
||||
collections::BTreeMap,
|
||||
ffi::OsStr,
|
||||
net::Ipv4Addr,
|
||||
net::{IpAddr, Ipv4Addr},
|
||||
path::{Path, PathBuf},
|
||||
sync::{Arc, Once},
|
||||
};
|
||||
|
|
@ -323,7 +323,7 @@ impl DapStore {
|
|||
let port_forwarding;
|
||||
let connection;
|
||||
if let Some(c) = binary.connection {
|
||||
let host = Ipv4Addr::LOCALHOST;
|
||||
let host = IpAddr::V4(Ipv4Addr::LOCALHOST);
|
||||
let port;
|
||||
if remote.read_with(cx, |remote, _cx| remote.shares_network_interface()) {
|
||||
port = c.port;
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ use serde_json::Value;
|
|||
use smol::net::{TcpListener, TcpStream};
|
||||
use std::any::TypeId;
|
||||
use std::collections::{BTreeMap, VecDeque};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::{IpAddr, Ipv4Addr};
|
||||
use std::ops::RangeInclusive;
|
||||
use std::path::PathBuf;
|
||||
use std::time::Duration;
|
||||
|
|
@ -2901,7 +2901,7 @@ impl Session {
|
|||
);
|
||||
None
|
||||
} else {
|
||||
let port = TcpTransport::unused_port(Ipv4Addr::LOCALHOST)
|
||||
let port = TcpTransport::unused_port(IpAddr::V4(Ipv4Addr::LOCALHOST))
|
||||
.await
|
||||
.context("getting port for DAP")?;
|
||||
request
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ use gpui::SharedString;
|
|||
use log as _;
|
||||
use schemars::JsonSchema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::net::Ipv4Addr;
|
||||
use std::net::IpAddr;
|
||||
use std::path::PathBuf;
|
||||
use util::{debug_panic, schemars::add_new_subschema};
|
||||
|
||||
|
|
@ -20,7 +20,7 @@ pub struct TcpArgumentsTemplate {
|
|||
/// The host that the debug adapter is listening too
|
||||
///
|
||||
/// Default: 127.0.0.1
|
||||
pub host: Option<Ipv4Addr>,
|
||||
pub host: Option<IpAddr>,
|
||||
/// The max amount of time in milliseconds to connect to a tcp DAP before returning an error
|
||||
///
|
||||
/// Default: 2000ms
|
||||
|
|
@ -29,8 +29,9 @@ pub struct TcpArgumentsTemplate {
|
|||
|
||||
impl TcpArgumentsTemplate {
|
||||
/// Get the host or fallback to the default host
|
||||
pub fn host(&self) -> Ipv4Addr {
|
||||
self.host.unwrap_or_else(|| Ipv4Addr::new(127, 0, 0, 1))
|
||||
pub fn host(&self) -> IpAddr {
|
||||
self.host
|
||||
.unwrap_or(IpAddr::V4(std::net::Ipv4Addr::LOCALHOST))
|
||||
}
|
||||
|
||||
pub fn from_proto(proto: proto::TcpHost) -> Result<Self> {
|
||||
|
|
@ -389,8 +390,7 @@ impl DebugTaskFile {
|
|||
},
|
||||
"host": {
|
||||
"type": "string",
|
||||
"pattern": "^((25[0-5]|(2[0-4]|1\\d|[1-9]|)\\d)\\.?\\b){4}$",
|
||||
"description": "The host that the debug adapter is listening to (default: 127.0.0.1)"
|
||||
"description": "The host that the debug adapter is listening to, as an IPv4 or IPv6 address (default: 127.0.0.1)"
|
||||
},
|
||||
"timeout": {
|
||||
"type": "integer",
|
||||
|
|
|
|||
Loading…
Reference in a new issue