blob: 57c0272bbf2cfced7f4a9d8d10dc237f85e34241 [file] [log] [blame]
// Copyright 2020 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
use {
anyhow::Result,
ffx_core::ffx_plugin,
ffx_target_add_args::AddCommand,
fidl_fuchsia_developer_bridge::{self as bridge, TargetCollectionProxy},
fidl_fuchsia_net as net,
regex::Regex,
std::net::IpAddr,
};
#[cfg(not(test))]
use std::ffi::CString;
#[ffx_plugin(TargetCollectionProxy = "daemon::service")]
pub async fn add(target_collection_proxy: TargetCollectionProxy, cmd: AddCommand) -> Result<()> {
let v6bracket = Regex::new(r"^\[([^\]]+)\](:\d+)?$")?;
let v4port = Regex::new(r"^(\d+\.\d+\.\d+\.\d+)(:\d+)?$")?;
let with_scope = Regex::new(r"^(.*)%(.*)$")?;
let (addr, port) = if let Some(caps) =
v6bracket.captures(cmd.addr.as_str()).or_else(|| v4port.captures(cmd.addr.as_str()))
{
(caps.get(1).map(|x| x.as_str()).unwrap(), caps.get(2).map(|x| x.as_str()))
} else {
(cmd.addr.as_str(), None)
};
let port = if let Some(port) = port { Some(port[1..].parse::<u16>()?) } else { None };
let (addr, scope) = if let Some(caps) = with_scope.captures(addr) {
(caps.get(1).map(|x| x.as_str()).unwrap(), Some(caps.get(2).map(|x| x.as_str()).unwrap()))
} else {
(addr, None)
};
let addr = addr.parse::<IpAddr>()?;
#[cfg(not(test))]
let scope_id = if let Some(scope) = scope {
unsafe {
let scope = CString::new(scope).unwrap();
libc::if_nametoindex(scope.as_ptr())
}
} else {
0
};
#[cfg(test)]
let scope_id = if let Some(scope) = scope { scope.parse()? } else { 0 };
let ip = match addr {
IpAddr::V6(i) => net::IpAddress::Ipv6(net::Ipv6Address { addr: i.octets().into() }),
IpAddr::V4(i) => net::IpAddress::Ipv4(net::Ipv4Address { addr: i.octets().into() }),
};
let mut addr = if let Some(port) = port {
bridge::TargetAddrInfo::IpPort(bridge::TargetIpPort { ip, port, scope_id })
} else {
bridge::TargetAddrInfo::Ip(bridge::TargetIp { ip, scope_id })
};
target_collection_proxy.add_target(&mut addr).await?;
Ok(())
}
////////////////////////////////////////////////////////////////////////////////
// tests
#[cfg(test)]
mod test {
use super::*;
fn setup_fake_target_collection<T: 'static + Fn(bridge::TargetAddrInfo) + Send>(
test: T,
) -> TargetCollectionProxy {
setup_fake_target_collection_proxy(move |req| match req {
bridge::TargetCollectionRequest::AddTarget { ip, responder } => {
test(ip);
responder.send().unwrap();
}
_ => assert!(false),
})
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_add() {
let server = setup_fake_target_collection(|addr| {
assert_eq!(
addr,
bridge::TargetAddrInfo::Ip(bridge::TargetIp {
ip: net::IpAddress::Ipv4(net::Ipv4Address {
addr: "123.210.123.210"
.parse::<std::net::Ipv4Addr>()
.unwrap()
.octets()
.into()
}),
scope_id: 0,
})
)
});
add(server, AddCommand { addr: "123.210.123.210".to_owned() }).await.unwrap();
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_add_port() {
let server = setup_fake_target_collection(|addr| {
assert_eq!(
addr,
bridge::TargetAddrInfo::IpPort(bridge::TargetIpPort {
ip: net::IpAddress::Ipv4(net::Ipv4Address {
addr: "123.210.123.210"
.parse::<std::net::Ipv4Addr>()
.unwrap()
.octets()
.into()
}),
scope_id: 0,
port: 2310,
})
)
});
add(server, AddCommand { addr: "123.210.123.210:2310".to_owned() }).await.unwrap();
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_add_v6() {
let server = setup_fake_target_collection(|addr| {
assert_eq!(
addr,
bridge::TargetAddrInfo::Ip(bridge::TargetIp {
ip: net::IpAddress::Ipv6(net::Ipv6Address {
addr: "f000::1".parse::<std::net::Ipv6Addr>().unwrap().octets().into()
}),
scope_id: 0,
})
)
});
add(server, AddCommand { addr: "f000::1".to_owned() }).await.unwrap();
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_add_v6_port() {
let server = setup_fake_target_collection(|addr| {
assert_eq!(
addr,
bridge::TargetAddrInfo::IpPort(bridge::TargetIpPort {
ip: net::IpAddress::Ipv6(net::Ipv6Address {
addr: "f000::1".parse::<std::net::Ipv6Addr>().unwrap().octets().into()
}),
scope_id: 0,
port: 65,
})
)
});
add(server, AddCommand { addr: "[f000::1]:65".to_owned() }).await.unwrap();
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_add_v6_scope_id() {
let server = setup_fake_target_collection(|addr| {
assert_eq!(
addr,
bridge::TargetAddrInfo::Ip(bridge::TargetIp {
ip: net::IpAddress::Ipv6(net::Ipv6Address {
addr: "f000::1".parse::<std::net::Ipv6Addr>().unwrap().octets().into()
}),
scope_id: 1,
})
)
});
add(server, AddCommand { addr: "f000::1%1".to_owned() }).await.unwrap();
}
#[fuchsia_async::run_singlethreaded(test)]
async fn test_add_v6_scope_id_port() {
let server = setup_fake_target_collection(|addr| {
assert_eq!(
addr,
bridge::TargetAddrInfo::IpPort(bridge::TargetIpPort {
ip: net::IpAddress::Ipv6(net::Ipv6Address {
addr: "f000::1".parse::<std::net::Ipv6Addr>().unwrap().octets().into()
}),
scope_id: 1,
port: 640,
})
)
});
add(server, AddCommand { addr: "[f000::1%1]:640".to_owned() }).await.unwrap();
}
}