diff --git a/examples/onoff_light/src/main.rs b/examples/onoff_light/src/main.rs index 08dece3..d480188 100644 --- a/examples/onoff_light/src/main.rs +++ b/examples/onoff_light/src/main.rs @@ -48,7 +48,8 @@ fn main() { device_name: "OnOff Light", }; - let mut mdns = matter::sys::LinuxMdns::new().unwrap(); + //let mut mdns = matter::mdns::bonjour::BonjourMdns::new().unwrap(); + let mut mdns = matter::mdns::libmdns::LibMdns::new().unwrap(); let matter = Matter::new_default(&dev_info, &mut mdns); diff --git a/matter/Cargo.toml b/matter/Cargo.toml index db10e77..85d113d 100644 --- a/matter/Cargo.toml +++ b/matter/Cargo.toml @@ -16,7 +16,7 @@ path = "src/lib.rs" [features] default = ["std", "crypto_mbedtls", "nightly"] -std = ["alloc", "env_logger", "chrono", "rand", "qrcode", "smol"] +std = ["alloc", "env_logger", "chrono", "rand", "qrcode", "libmdns", "simple-mdns", "simple-dns", "smol"] alloc = [] nightly = [] crypto_openssl = ["openssl", "foreign-types", "hmac", "sha2"] @@ -43,8 +43,12 @@ colored = "2.0.0" # TODO: Requires STD env_logger = { version = "0.10.0", default-features = false, optional = true } chrono = { version = "0.4.23", optional = true, default-features = false, features = ["clock", "std"] } rand = { version = "0.8.5", optional = true } -smol = { version = "1.3.0", optional = true} qrcode = { version = "0.12", default-features = false, optional = true } # Print QR code +libmdns = { version = "0.7", optional = true } +simple-mdns = { version = "0.4", features = ["sync"], optional = true } +simple-dns = { version = "0.5", optional = true } +astro-dnssd = { version = "0.3", optional = true } +smol = { version = "1.3.0", optional = true} # crypto openssl = { git = "https://github.com/sfackler/rust-openssl", optional = true } @@ -66,13 +70,6 @@ x509-cert = { version = "0.2.0", default-features = false, features = ["pem", "s # to compute the check digit verhoeff = "1" -[target.'cfg(target_os = "macos")'.dependencies] -astro-dnssd = "0.3" - -# MDNS support -[target.'cfg(target_os = "linux")'.dependencies] -libmdns = { version = "0.7.4" } - [[example]] name = "onoff_light" path = "../examples/onoff_light/src/main.rs" diff --git a/matter/src/mdns.rs b/matter/src/mdns.rs index 71be231..1f21866 100644 --- a/matter/src/mdns.rs +++ b/matter/src/mdns.rs @@ -25,6 +25,7 @@ pub trait Mdns { name: &str, service_type: &str, port: u16, + service_subtypes: &[&str], txt_kvs: &[(&str, &str)], ) -> Result<(), Error>; @@ -40,9 +41,10 @@ where name: &str, service_type: &str, port: u16, + service_subtypes: &[&str], txt_kvs: &[(&str, &str)], ) -> Result<(), Error> { - (**self).add(name, service_type, port, txt_kvs) + (**self).add(name, service_type, port, service_subtypes, txt_kvs) } fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { @@ -58,6 +60,7 @@ impl Mdns for DummyMdns { _name: &str, _service_type: &str, _port: u16, + _service_subtypes: &[&str], _txt_kvs: &[(&str, &str)], ) -> Result<(), Error> { Ok(()) @@ -112,11 +115,12 @@ impl<'a> MdnsMgr<'a> { #[allow(clippy::needless_pass_by_value)] pub fn publish_service(&mut self, name: &str, mode: ServiceMode) -> Result<(), Error> { match mode { - ServiceMode::Commissioned => self.mdns.add(name, "_matter._tcp", self.matter_port, &[]), + ServiceMode::Commissioned => { + self.mdns + .add(name, "_matter._tcp", self.matter_port, &[], &[]) + } ServiceMode::Commissionable(discriminator) => { let discriminator_str = Self::get_discriminator_str(discriminator); - - let serv_type = self.get_service_type(discriminator); let vp = self.get_vp(); let txt_kvs = [ @@ -129,7 +133,17 @@ impl<'a> MdnsMgr<'a> { ("PH", "33"), /* Pairing Hint */ ("PI", ""), /* Pairing Instruction */ ]; - self.mdns.add(name, &serv_type, self.matter_port, &txt_kvs) + + self.mdns.add( + name, + "_matter._udp", + self.matter_port, + &[ + &self.get_long_service_subtype(discriminator), + &self.get_short_service_type(discriminator), + ], + &txt_kvs, + ) } } } @@ -137,28 +151,32 @@ impl<'a> MdnsMgr<'a> { pub fn unpublish_service(&mut self, name: &str, mode: ServiceMode) -> Result<(), Error> { match mode { ServiceMode::Commissioned => self.mdns.remove(name, "_matter._tcp", self.matter_port), - ServiceMode::Commissionable(discriminator) => { - let serv_type = self.get_service_type(discriminator); - - self.mdns.remove(name, &serv_type, self.matter_port) + ServiceMode::Commissionable(_) => { + self.mdns.remove(name, "_matter._udp", self.matter_port) } } } - fn get_service_type(&self, discriminator: u16) -> heapless::String<32> { - let short = Self::compute_short_discriminator(discriminator); + fn get_long_service_subtype(&self, discriminator: u16) -> heapless::String<32> { let mut serv_type = heapless::String::new(); - - write!( - &mut serv_type, - "_matterc._udp,_S{},_L{}", - short, discriminator - ) - .unwrap(); + write!(&mut serv_type, "_L{}", discriminator).unwrap(); serv_type } + fn get_short_service_type(&self, discriminator: u16) -> heapless::String<32> { + let short = Self::compute_short_discriminator(discriminator); + + let mut serv_type = heapless::String::new(); + write!(&mut serv_type, "_S{}", short).unwrap(); + + serv_type + } + + fn get_discriminator_str(discriminator: u16) -> heapless::String<5> { + discriminator.into() + } + fn get_vp(&self) -> heapless::String<11> { let mut vp = heapless::String::new(); @@ -167,10 +185,6 @@ impl<'a> MdnsMgr<'a> { vp } - fn get_discriminator_str(discriminator: u16) -> heapless::String<5> { - discriminator.into() - } - fn compute_short_discriminator(discriminator: u16) -> u16 { const SHORT_DISCRIMINATOR_MASK: u16 = 0xF00; const SHORT_DISCRIMINATOR_SHIFT: u16 = 8; @@ -179,6 +193,353 @@ impl<'a> MdnsMgr<'a> { } } +#[cfg(all(feature = "std", feature = "bonjour"))] +pub mod bonjour { + use std::collections::HashMap; + + use super::Mdns; + use crate::error::Error; + use astro_dnssd::{DNSServiceBuilder, RegisteredDnsService}; + use log::info; + + #[derive(Debug, Clone, Eq, PartialEq, Hash)] + pub struct ServiceId { + name: String, + service_type: String, + port: u16, + } + + pub struct BonjourMdns { + services: HashMap, + } + + impl BonjourMdns { + pub fn new() -> Result { + Ok(Self { + services: HashMap::new(), + }) + } + + pub fn add( + &mut self, + name: &str, + service_type: &str, + port: u16, + service_subtypes: &[&str], + txt_kvs: &[(&str, &str)], + ) -> Result<(), Error> { + info!( + "Registering mDNS service {}/{}/{}", + name, service_type, port + ); + + let _ = self.remove(name, service_type, port); + + let composite_service_type = if !service_subtypes.is_empty() { + format!("{}{}", service_type, service_subtypes.join(",")) + } else { + service_type + }; + + let mut builder = DNSServiceBuilder::new(composite_service_type, port).with_name(name); + + for kvs in txt_kvs { + info!("mDNS TXT key {} val {}", kvs.0, kvs.1); + builder = builder.with_key_value(kvs.0.to_string(), kvs.1.to_string()); + } + + let service = builder.register().map_err(|_| Error::MdnsError)?; + + self.services.insert( + ServiceId { + name: name.into(), + service_type: service_type.into(), + port, + }, + service, + ); + + Ok(()) + } + + pub fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { + let id = ServiceId { + name: name.into(), + service_type: service_type.into(), + port, + }; + + if self.services.remove(&id).is_some() { + info!( + "Deregistering mDNS service {}/{}/{}", + name, service_type, port + ); + } + + Ok(()) + } + } + + impl Mdns for BonjourMdns { + fn add( + &mut self, + name: &str, + service_type: &str, + port: u16, + service_subtypes: &[&str], + txt_kvs: &[(&str, &str)], + ) -> Result<(), Error> { + BonjourMdns::add(self, name, service_type, port, service_subtypes, txt_kvs) + } + + fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { + BonjourMdns::remove(self, name, service_type, port) + } + } +} + +#[cfg(feature = "std")] +pub mod libmdns { + use super::Mdns; + use crate::error::Error; + use libmdns::{Responder, Service}; + use log::info; + use std::collections::HashMap; + use std::vec::Vec; + + #[derive(Debug, Clone, Eq, PartialEq, Hash)] + pub struct ServiceId { + name: String, + service_type: String, + port: u16, + } + + pub struct LibMdns { + responder: Responder, + services: HashMap, + } + + impl LibMdns { + pub fn new() -> Result { + let responder = Responder::new()?; + + Ok(Self { + responder, + services: HashMap::new(), + }) + } + + pub fn add( + &mut self, + name: &str, + service_type: &str, + port: u16, + txt_kvs: &[(&str, &str)], + ) -> Result<(), Error> { + info!( + "Registering mDNS service {}/{}/{}", + name, service_type, port + ); + + let _ = self.remove(name, service_type, port); + + let mut properties = Vec::new(); + for kvs in txt_kvs { + info!("mDNS TXT key {} val {}", kvs.0, kvs.1); + properties.push(format!("{}={}", kvs.0, kvs.1)); + } + let properties: Vec<&str> = properties.iter().map(|entry| entry.as_str()).collect(); + + let service = self.responder.register( + service_type.to_owned(), + name.to_owned(), + port, + &properties, + ); + + self.services.insert( + ServiceId { + name: name.into(), + service_type: service_type.into(), + port, + }, + service, + ); + + Ok(()) + } + + pub fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { + let id = ServiceId { + name: name.into(), + service_type: service_type.into(), + port, + }; + + if self.services.remove(&id).is_some() { + info!( + "Deregistering mDNS service {}/{}/{}", + name, service_type, port + ); + } + + Ok(()) + } + } + + impl Mdns for LibMdns { + fn add( + &mut self, + name: &str, + service_type: &str, + port: u16, + _service_subtypes: &[&str], + txt_kvs: &[(&str, &str)], + ) -> Result<(), Error> { + LibMdns::add(self, name, service_type, port, txt_kvs) + } + + fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { + LibMdns::remove(self, name, service_type, port) + } + } +} + +// #[cfg(feature = "std")] +// pub mod simplemdns { +// use std::net::Ipv4Addr; + +// use crate::error::Error; +// use super::Mdns; +// use log::info; +// use simple_dns::{ +// rdata::{RData, A, SRV, TXT, PTR}, +// CharacterString, Name, ResourceRecord, CLASS, +// }; +// use simple_mdns::sync_discovery::SimpleMdnsResponder; + +// #[derive(Debug, Clone, Eq, PartialEq, Hash)] +// pub struct ServiceId { +// name: String, +// service_type: String, +// port: u16, +// } + +// pub struct SimpleMdns { +// responder: SimpleMdnsResponder, +// } + +// impl SimpleMdns { +// pub fn new() -> Result { +// Ok(Self { +// responder: Default::default(), +// }) +// } + +// pub fn add( +// &mut self, +// name: &str, +// service_type: &str, +// port: u16, +// txt_kvs: &[(&str, &str)], +// ) -> Result<(), Error> { +// info!( +// "Registering mDNS service {}/{}/{}", +// name, service_type, port +// ); + +// let _ = self.remove(name, service_type, port); + +// let mut txt = TXT::new(); +// for kvs in txt_kvs { +// info!("mDNS TXT key {} val {}", kvs.0, kvs.1); + +// let string = format!("{}={}", kvs.0, kvs.1); +// txt.add_char_string( +// CharacterString::new(string.as_bytes()) +// .unwrap() +// .into_owned(), +// ); +// } + +// let name = Name::new_unchecked(name).into_owned(); +// let service_type = Name::new_unchecked(service_type).into_owned(); + +// self.responder.add_resource(ResourceRecord::new( +// name.clone(), +// CLASS::IN, +// 10, +// RData::A(A { +// address: Ipv4Addr::new(192, 168, 10, 189).into(), +// }), +// )); + +// self.responder.add_resource(ResourceRecord::new( +// name.clone(), +// CLASS::IN, +// 10, +// RData::SRV(SRV { +// port: port, +// priority: 0, +// weight: 0, +// target: service_type.clone(), +// }), +// )); + +// self.responder.add_resource(ResourceRecord::new( +// srv_name.clone(), +// CLASS::IN, +// 10, +// RData::PTR(PTR(srv_name.clone()), +// ))); + +// self.responder.add_resource(ResourceRecord::new( +// srv_name, +// CLASS::IN, +// 10, +// RData::TXT(txt), +// )); + +// Ok(()) +// } + +// pub fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { +// // TODO +// // let id = ServiceId { +// // name: name.into(), +// // service_type: service_type.into(), +// // port, +// // }; + +// // if self.responder.remove_resource_record(resource).remove(&id).is_some() { +// // info!( +// // "Deregistering mDNS service {}/{}/{}", +// // name, service_type, port +// // ); +// // } + +// Ok(()) +// } +// } + +// impl Mdns for SimpleMdns { +// fn add( +// &mut self, +// name: &str, +// service_type: &str, +// port: u16, +// _service_subtypes: &[&str], +// txt_kvs: &[(&str, &str)], +// ) -> Result<(), Error> { +// SimpleMdns::add(self, name, service_type, port, txt_kvs) +// } + +// fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { +// SimpleMdns::remove(self, name, service_type, port) +// } +// } +// } + #[cfg(test)] mod tests { use super::*; diff --git a/matter/src/pairing/qr.rs b/matter/src/pairing/qr.rs index 33f7b8a..e99d909 100644 --- a/matter/src/pairing/qr.rs +++ b/matter/src/pairing/qr.rs @@ -315,10 +315,7 @@ fn estimate_struct_overhead(first_field_size: usize) -> usize { } pub(super) fn print_qr_code(qr_data: &str) { - #[cfg(not(feature = "std"))] - { - info!("\n QR CODE DATA: {}", qr_data); - } + info!("QR Code: {}", qr_data); #[cfg(feature = "std")] { diff --git a/matter/src/secure_channel/spake2p.rs b/matter/src/secure_channel/spake2p.rs index ba948f5..8a4b794 100644 --- a/matter/src/secure_channel/spake2p.rs +++ b/matter/src/secure_channel/spake2p.rs @@ -17,7 +17,6 @@ use crate::{ crypto::{self, HmacSha256}, - sys, utils::rand::Rand, }; use byteorder::{ByteOrder, LittleEndian}; @@ -31,7 +30,7 @@ use crate::{ use super::{common::SCStatusCodes, crypto::CryptoSpake2}; -// This file handle Spake2+ specific instructions. In itself, this file is +// This file handles Spake2+ specific instructions. In itself, this file is // independent from the BigNum and EC operations that are typically required // Spake2+. We use the CryptoSpake2 trait object that allows us to abstract // out the specific implementations. @@ -39,6 +38,8 @@ use super::{common::SCStatusCodes, crypto::CryptoSpake2}; // In the case of the verifier, we don't actually release the Ke until we // validate that the cA is confirmed. +pub const SPAKE2_ITERATION_COUNT: u32 = 2000; + #[derive(PartialEq, Copy, Clone, Debug)] pub enum Spake2VerifierState { // Initialised - w0, L are set @@ -104,7 +105,7 @@ impl VerifierData { pub fn new_with_pw(pw: u32, rand: Rand) -> Self { let mut s = Self { salt: [0; MAX_SALT_SIZE_BYTES], - count: sys::SPAKE2_ITERATION_COUNT, + count: SPAKE2_ITERATION_COUNT, data: VerifierOption::Password(pw), }; rand(&mut s.salt); diff --git a/matter/src/sys/mod.rs b/matter/src/sys/mod.rs index 0ce65e7..a80b875 100644 --- a/matter/src/sys/mod.rs +++ b/matter/src/sys/mod.rs @@ -15,18 +15,6 @@ * limitations under the License. */ -#[cfg(all(feature = "std", target_os = "macos"))] -mod sys_macos; -#[cfg(all(feature = "std", target_os = "macos"))] -pub use self::sys_macos::*; - -#[cfg(all(feature = "std", target_os = "linux"))] -mod sys_linux; -#[cfg(all(feature = "std", target_os = "linux"))] -pub use self::sys_linux::*; - -pub const SPAKE2_ITERATION_COUNT: u32 = 2000; - // The Packet Pool that is allocated from. POSIX systems can use // higher values unlike embedded systems pub const MAX_PACKET_POOL_SIZE: usize = 25; diff --git a/matter/src/sys/sys_linux.rs b/matter/src/sys/sys_linux.rs deleted file mode 100644 index 0d3f0dc..0000000 --- a/matter/src/sys/sys_linux.rs +++ /dev/null @@ -1,116 +0,0 @@ -/* - * - * Copyright (c) 2020-2022 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use crate::error::Error; -use crate::mdns::Mdns; -use libmdns::{Responder, Service}; -use log::info; -use std::collections::HashMap; -use std::vec::Vec; - -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct ServiceId { - name: String, - service_type: String, - port: u16, -} - -pub struct LinuxMdns { - responder: Responder, - services: HashMap, -} - -impl LinuxMdns { - pub fn new() -> Result { - let responder = Responder::new()?; - - Ok(Self { - responder, - services: HashMap::new(), - }) - } - - pub fn add( - &mut self, - name: &str, - service_type: &str, - port: u16, - txt_kvs: &[(&str, &str)], - ) -> Result<(), Error> { - info!( - "Registering mDNS service {}/{}/{}", - name, service_type, port - ); - - let _ = self.remove(name, service_type, port); - - let mut properties = Vec::new(); - for kvs in txt_kvs { - info!("mDNS TXT key {} val {}", kvs.0, kvs.1); - properties.push(format!("{}={}", kvs.0, kvs.1)); - } - let properties: Vec<&str> = properties.iter().map(|entry| entry.as_str()).collect(); - - let service = - self.responder - .register(service_type.to_owned(), name.to_owned(), port, &properties); - - self.services.insert( - ServiceId { - name: name.into(), - service_type: service_type.into(), - port, - }, - service, - ); - - Ok(()) - } - - pub fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { - let id = ServiceId { - name: name.into(), - service_type: service_type.into(), - port, - }; - - if self.services.remove(&id).is_some() { - info!( - "Deregistering mDNS service {}/{}/{}", - name, service_type, port - ); - } - - Ok(()) - } -} - -impl Mdns for LinuxMdns { - fn add( - &mut self, - name: &str, - service_type: &str, - port: u16, - txt_kvs: &[(&str, &str)], - ) -> Result<(), Error> { - LinuxMdns::add(self, name, service_type, port, txt_kvs) - } - - fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { - LinuxMdns::remove(self, name, service_type, port) - } -} diff --git a/matter/src/sys/sys_macos.rs b/matter/src/sys/sys_macos.rs deleted file mode 100644 index d8ffe3a..0000000 --- a/matter/src/sys/sys_macos.rs +++ /dev/null @@ -1,109 +0,0 @@ -/* - * - * Copyright (c) 2020-2022 Project CHIP Authors - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -use std::collections::HashMap; - -use crate::{error::Error, mdns::Mdns}; -use astro_dnssd::{DNSServiceBuilder, RegisteredDnsService}; -use log::info; - -#[derive(Debug, Clone, Eq, PartialEq, Hash)] -pub struct ServiceId { - name: String, - service_type: String, - port: u16, -} - -pub struct MacOsMdns { - services: HashMap, -} - -impl MacOsMdns { - pub fn new() -> Result { - Ok(Self { - services: HashMap::new(), - }) - } - - pub fn add( - &mut self, - name: &str, - service_type: &str, - port: u16, - txt_kvs: &[(&str, &str)], - ) -> Result<(), Error> { - info!( - "Registering mDNS service {}/{}/{}", - name, service_type, port - ); - - let _ = self.remove(name, service_type, port); - - let mut builder = DNSServiceBuilder::new(service_type, port).with_name(name); - - for kvs in txt_kvs { - info!("mDNS TXT key {} val {}", kvs.0, kvs.1); - builder = builder.with_key_value(kvs.0.to_string(), kvs.1.to_string()); - } - - let service = builder.register().map_err(|_| Error::MdnsError)?; - - self.services.insert( - ServiceId { - name: name.into(), - service_type: service_type.into(), - port, - }, - service, - ); - - Ok(()) - } - - pub fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { - let id = ServiceId { - name: name.into(), - service_type: service_type.into(), - port, - }; - - if self.services.remove(&id).is_some() { - info!( - "Deregistering mDNS service {}/{}/{}", - name, service_type, port - ); - } - - Ok(()) - } -} - -impl Mdns for MacOsMdns { - fn add( - &mut self, - name: &str, - service_type: &str, - port: u16, - txt_kvs: &[(&str, &str)], - ) -> Result<(), Error> { - MacOsMdns::add(self, name, service_type, port, txt_kvs) - } - - fn remove(&mut self, name: &str, service_type: &str, port: u16) -> Result<(), Error> { - MacOsMdns::remove(self, name, service_type, port) - } -} diff --git a/matter/src/transport/udp.rs b/matter/src/transport/udp.rs index 7a82fb3..b2dd1dc 100644 --- a/matter/src/transport/udp.rs +++ b/matter/src/transport/udp.rs @@ -16,7 +16,7 @@ */ use crate::error::*; -use log::info; +use log::{info, warn}; use smol::net::{Ipv6Addr, UdpSocket}; use super::network::Address; @@ -35,25 +35,50 @@ pub const MATTER_PORT: u16 = 5540; impl UdpListener { pub async fn new() -> Result { - Ok(UdpListener { + let listener = UdpListener { socket: UdpSocket::bind((Ipv6Addr::UNSPECIFIED, MATTER_PORT)).await?, - }) + }; + + info!( + "Listening on {:?} port {}", + Ipv6Addr::UNSPECIFIED, + MATTER_PORT + ); + + Ok(listener) } pub async fn recv(&self, in_buf: &mut [u8]) -> Result<(usize, Address), Error> { + info!("Waiting for incoming packets"); + let (size, addr) = self.socket.recv_from(in_buf).await.map_err(|e| { - info!("Error on the network: {:?}", e); + warn!("Error on the network: {:?}", e); Error::Network })?; + + info!("Got packet: {:?} from addr {:?}", in_buf, addr); + Ok((size, Address::Udp(addr))) } pub async fn send(&self, addr: Address, out_buf: &[u8]) -> Result { match addr { - Address::Udp(addr) => self.socket.send_to(out_buf, addr).await.map_err(|e| { - info!("Error on the network: {:?}", e); - Error::Network - }), + Address::Udp(addr) => { + let len = self.socket.send_to(out_buf, addr).await.map_err(|e| { + warn!("Error on the network: {:?}", e); + Error::Network + })?; + + info!( + "Send packet: {:?} ({}/{}) to addr {:?}", + out_buf, + out_buf.len(), + len, + addr + ); + + Ok(len) + } } } }