| // Copyright 2019 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::{format_err, Error}; |
| use fidl::endpoints::RequestStream; |
| use fidl_fuchsia_net_mdns::{ |
| Media, Publication, PublicationResponder_Request, PublicationResponder_RequestStream, |
| PublisherMarker, PublisherProxy, ServiceSubscriberRequest, ServiceSubscriberRequestStream, |
| SubscriberMarker, |
| }; |
| use fuchsia_async as fasync; |
| use fuchsia_zircon as zx; |
| use futures::prelude::*; |
| use overnet_core::NodeId; |
| |
| const SERVICE_NAME: &str = "_overnet._udp."; |
| |
| async fn connect_to_proxy( |
| node_id: NodeId, |
| publisher: PublisherProxy, |
| port: u16, |
| proxy: zx::Channel, |
| ) -> Result<(), Error> { |
| log::info!("Publish overnet service on port {}", port); |
| |
| publisher |
| .publish_service_instance( |
| SERVICE_NAME, |
| &format!("{:?}", node_id.0), |
| Media::WIRED | Media::WIRELESS, |
| true, |
| fidl::endpoints::ClientEnd::new(proxy), |
| ) |
| .await? |
| .map_err(|e| format_err!("{:?}", e))?; |
| |
| log::info!("Published overnet service on port {}", port); |
| Ok(()) |
| } |
| |
| /// Run main loop to publish a udp socket to mdns. |
| pub async fn publish(port: u16, node_id: NodeId) -> Result<(), Error> { |
| let (server, proxy) = zx::Channel::create()?; |
| let server = fasync::Channel::from_channel(server)?; |
| |
| let publisher = fuchsia_component::client::connect_to_protocol::<PublisherMarker>()?; |
| |
| futures::future::try_join( |
| connect_to_proxy(node_id, publisher, port, proxy), |
| PublicationResponder_RequestStream::from_channel(server).map_err(Into::into).try_for_each( |
| |PublicationResponder_Request::OnPublication { responder, .. }| async move { |
| responder |
| .send(Some(&mut Publication { |
| port, |
| text: vec![], |
| srv_priority: fidl_fuchsia_net_mdns::DEFAULT_SRV_PRIORITY, |
| srv_weight: fidl_fuchsia_net_mdns::DEFAULT_SRV_WEIGHT, |
| ptr_ttl: fidl_fuchsia_net_mdns::DEFAULT_PTR_TTL, |
| srv_ttl: fidl_fuchsia_net_mdns::DEFAULT_SRV_TTL, |
| txt_ttl: fidl_fuchsia_net_mdns::DEFAULT_TXT_TTL, |
| })) |
| .map_err(Into::into) |
| }, |
| ), |
| ) |
| .await?; |
| |
| Ok(()) |
| } |
| |
| fn convert_ipv6_buffer(in_arr: [u8; 16]) -> [u16; 8] { |
| let mut out_arr: [u16; 8] = [0; 8]; |
| |
| for i in 0..8 { |
| out_arr[i] = ((in_arr[2 * i] as u16) << 8) | (in_arr[2 * i + 1] as u16); |
| } |
| |
| out_arr |
| } |
| |
| fn fuchsia_to_rust_ipaddr4(addr: fidl_fuchsia_net::Ipv4Address) -> std::net::Ipv4Addr { |
| std::net::Ipv4Addr::new(addr.addr[0], addr.addr[1], addr.addr[2], addr.addr[3]) |
| } |
| |
| fn fuchsia_to_rust_ipaddr6(addr: fidl_fuchsia_net::Ipv6Address) -> std::net::Ipv6Addr { |
| let addr = convert_ipv6_buffer(addr.addr); |
| std::net::Ipv6Addr::new(addr[0], addr[1], addr[2], addr[3], addr[4], addr[5], addr[6], addr[7]) |
| } |
| |
| fn endpoint4_to_socket(ep: fidl_fuchsia_net::Ipv4SocketAddress) -> std::net::SocketAddrV6 { |
| std::net::SocketAddrV6::new(fuchsia_to_rust_ipaddr4(ep.address).to_ipv6_mapped(), ep.port, 0, 0) |
| } |
| |
| fn endpoint6_to_socket(ep: fidl_fuchsia_net::Ipv6SocketAddress) -> std::net::SocketAddrV6 { |
| std::net::SocketAddrV6::new(fuchsia_to_rust_ipaddr6(ep.address), ep.port, 0, 0) |
| } |
| |
| /// Run main loop to look for overnet mdns advertisements and add them to the mesh. |
| pub async fn subscribe( |
| found: futures::channel::mpsc::Sender<std::net::SocketAddrV6>, |
| ) -> Result<(), Error> { |
| log::info!("Query for overnet services"); |
| |
| let (server, proxy) = zx::Channel::create()?; |
| fuchsia_component::client::connect_to_protocol::<SubscriberMarker>()? |
| .subscribe_to_service(SERVICE_NAME, fidl::endpoints::ClientEnd::new(proxy))?; |
| |
| log::info!("Wait for overnet services"); |
| let found = &found; |
| |
| ServiceSubscriberRequestStream::from_channel(fasync::Channel::from_channel(server)?) |
| .map_err(Into::into) |
| .try_for_each(|request| async move { |
| match request { |
| ServiceSubscriberRequest::OnInstanceDiscovered { instance, responder } => { |
| log::info!("Discovered: {:?}", instance); |
| if let Some(ipv4_endpoint) = instance.ipv4_endpoint { |
| found.clone().send(endpoint4_to_socket(ipv4_endpoint)).await?; |
| } |
| if let Some(ipv6_endpoint) = instance.ipv6_endpoint { |
| found.clone().send(endpoint6_to_socket(ipv6_endpoint)).await?; |
| } |
| responder.send()?; |
| } |
| ServiceSubscriberRequest::OnInstanceChanged { instance, responder } => { |
| log::info!("Changed: {:?}", instance); |
| if let Some(ipv4_endpoint) = instance.ipv4_endpoint { |
| found.clone().send(endpoint4_to_socket(ipv4_endpoint)).await?; |
| } |
| if let Some(ipv6_endpoint) = instance.ipv6_endpoint { |
| found.clone().send(endpoint6_to_socket(ipv6_endpoint)).await?; |
| } |
| responder.send()?; |
| } |
| ServiceSubscriberRequest::OnInstanceLost { responder, .. } => { |
| log::info!("Removed a thing"); |
| responder.send()?; |
| } |
| ServiceSubscriberRequest::OnQuery { responder, .. } => { |
| responder.send()?; |
| } |
| } |
| Ok::<_, Error>(()) |
| }) |
| .await?; |
| |
| log::info!("Mdns subscriber finishes"); |
| |
| Ok(()) |
| } |
| |
| // [START test_mod] |
| #[cfg(test)] |
| mod tests { |
| use super::*; |
| #[test] |
| fn test_fuchsia_to_rust_ipaddr6() { |
| //test example IPv6 address [fe80::5054:ff:fe40:5763] |
| //fidl_fuchsia_net::Ipv6Address |
| let ipv6_addr = fidl_fuchsia_net::Ipv6Address { |
| addr: [ |
| 0xff, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x50, 0x54, 0x00, 0xff, 0xfe, 0x40, |
| 0x57, 0x63, |
| ], |
| }; |
| |
| //std::net::IpAddr |
| let net_addr = std::net::IpAddr::V6(std::net::Ipv6Addr::new( |
| 0xff80, 0x0000, 0x0000, 0x0000, 0x5054, 0x00ff, 0xfe40, 0x5763, |
| )); |
| |
| //expected:[fuchsia_to_rust_ipaddr6(ipv6_addr)] == [net_addr] |
| assert_eq!(fuchsia_to_rust_ipaddr6(ipv6_addr), net_addr); |
| } |
| } |
| // [END test_mod] |