use crate::{dns, META_QUERY_SERVICE, SERVICE_NAME};
use dns_parser::{Packet, RData};
use libp2p_core::{
multiaddr::{Multiaddr, Protocol},
PeerId,
};
use std::{convert::TryFrom, fmt, net::SocketAddr, str, time::Duration};
#[derive(Debug)]
pub enum MdnsPacket {
Query(MdnsQuery),
Response(MdnsResponse),
ServiceDiscovery(MdnsServiceDiscovery),
}
impl MdnsPacket {
pub fn new_from_bytes(buf: &[u8], from: SocketAddr) -> Option<MdnsPacket> {
match Packet::parse(buf) {
Ok(packet) => {
if packet.header.query {
if packet
.questions
.iter()
.any(|q| q.qname.to_string().as_bytes() == SERVICE_NAME)
{
let query = MdnsPacket::Query(MdnsQuery {
from,
query_id: packet.header.id,
});
Some(query)
} else if packet
.questions
.iter()
.any(|q| q.qname.to_string().as_bytes() == META_QUERY_SERVICE)
{
let discovery = MdnsPacket::ServiceDiscovery(MdnsServiceDiscovery {
from,
query_id: packet.header.id,
});
Some(discovery)
} else {
None
}
} else {
let resp = MdnsPacket::Response(MdnsResponse::new(packet, from));
Some(resp)
}
}
Err(err) => {
log::debug!("Parsing mdns packet failed: {:?}", err);
None
}
}
}
}
pub struct MdnsQuery {
from: SocketAddr,
query_id: u16,
}
impl MdnsQuery {
pub fn remote_addr(&self) -> &SocketAddr {
&self.from
}
pub fn query_id(&self) -> u16 {
self.query_id
}
}
impl fmt::Debug for MdnsQuery {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MdnsQuery")
.field("from", self.remote_addr())
.field("query_id", &self.query_id)
.finish()
}
}
pub struct MdnsServiceDiscovery {
from: SocketAddr,
query_id: u16,
}
impl MdnsServiceDiscovery {
pub fn remote_addr(&self) -> &SocketAddr {
&self.from
}
pub fn query_id(&self) -> u16 {
self.query_id
}
}
impl fmt::Debug for MdnsServiceDiscovery {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MdnsServiceDiscovery")
.field("from", self.remote_addr())
.field("query_id", &self.query_id)
.finish()
}
}
pub struct MdnsResponse {
peers: Vec<MdnsPeer>,
from: SocketAddr,
}
impl MdnsResponse {
pub fn new(packet: Packet<'_>, from: SocketAddr) -> MdnsResponse {
let peers = packet
.answers
.iter()
.filter_map(|record| {
if record.name.to_string().as_bytes() != SERVICE_NAME {
return None;
}
let record_value = match record.data {
RData::PTR(record) => record.0.to_string(),
_ => return None,
};
let mut peer_name = match record_value.rsplitn(4, |c| c == '.').last() {
Some(n) => n.to_owned(),
None => return None,
};
peer_name.retain(|c| c != '.');
let peer_id = match data_encoding::BASE32_DNSCURVE.decode(peer_name.as_bytes()) {
Ok(bytes) => match PeerId::from_bytes(&bytes) {
Ok(id) => id,
Err(_) => return None,
},
Err(_) => return None,
};
Some(MdnsPeer::new(&packet, record_value, peer_id, record.ttl))
})
.collect();
MdnsResponse { peers, from }
}
pub fn discovered_peers(&self) -> impl Iterator<Item = &MdnsPeer> {
self.peers.iter()
}
#[inline]
pub fn remote_addr(&self) -> &SocketAddr {
&self.from
}
}
impl fmt::Debug for MdnsResponse {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MdnsResponse")
.field("from", self.remote_addr())
.finish()
}
}
pub struct MdnsPeer {
addrs: Vec<Multiaddr>,
peer_id: PeerId,
ttl: u32,
}
impl MdnsPeer {
pub fn new(
packet: &Packet<'_>,
record_value: String,
my_peer_id: PeerId,
ttl: u32,
) -> MdnsPeer {
let addrs = packet
.additional
.iter()
.filter_map(|add_record| {
if add_record.name.to_string() != record_value {
return None;
}
if let RData::TXT(ref txt) = add_record.data {
Some(txt)
} else {
None
}
})
.flat_map(|txt| txt.iter())
.filter_map(|txt| {
let addr = match dns::decode_character_string(txt) {
Ok(a) => a,
Err(_) => return None,
};
if !addr.starts_with(b"dnsaddr=") {
return None;
}
let addr = match str::from_utf8(&addr[8..]) {
Ok(a) => a,
Err(_) => return None,
};
let mut addr = match addr.parse::<Multiaddr>() {
Ok(a) => a,
Err(_) => return None,
};
match addr.pop() {
Some(Protocol::P2p(peer_id)) => {
if let Ok(peer_id) = PeerId::try_from(peer_id) {
if peer_id != my_peer_id {
return None;
}
} else {
return None;
}
}
_ => return None,
};
Some(addr)
})
.collect();
MdnsPeer {
addrs,
peer_id: my_peer_id,
ttl,
}
}
#[inline]
pub fn id(&self) -> &PeerId {
&self.peer_id
}
#[inline]
pub fn ttl(&self) -> Duration {
Duration::from_secs(u64::from(self.ttl))
}
pub fn addresses(&self) -> &Vec<Multiaddr> {
&self.addrs
}
}
impl fmt::Debug for MdnsPeer {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.debug_struct("MdnsPeer")
.field("peer_id", &self.peer_id)
.finish()
}
}