1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
#![doc = include_str!("../README.md")]
#![warn(missing_docs)]
#[macro_use]
extern crate lazy_static;

use std::{
    io,
    net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket},
    time::Duration,
};

use simple_dns::SimpleDnsError;
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
use thiserror::Error;

pub mod conversion_utils;
mod dns_packet_receiver;
mod oneshot_resolver;
mod resource_record_manager;
mod service_discovery;
mod simple_responder;

pub use oneshot_resolver::OneShotMdnsResolver;
pub use service_discovery::{InstanceInformation, ServiceDiscovery};
pub use simple_responder::SimpleMdnsResponder;

const UNICAST_RESPONSE: bool = cfg!(not(test));

const MULTICAST_PORT: u16 = 5353;
const MULTICAST_ADDR_IPV4: Ipv4Addr = Ipv4Addr::new(224, 0, 0, 251);
const MULTICAST_ADDR_IPV6: Ipv6Addr = Ipv6Addr::new(0xFF02, 0, 0, 0, 0, 0, 0, 0xFB);

lazy_static! {
    pub(crate) static ref MULTICAST_IPV4_SOCKET: SocketAddr =
        SocketAddr::new(IpAddr::V4(MULTICAST_ADDR_IPV4), MULTICAST_PORT);
    pub(crate) static ref MULTICAST_IPV6_SOCKET: SocketAddr =
        SocketAddr::new(IpAddr::V6(MULTICAST_ADDR_IPV6), MULTICAST_PORT);
}

/// Error types for simple-mdns
#[derive(Debug, Error)]
pub enum SimpleMdnsError {
    /// Udp socket related error
    #[error("There was an error related to UDP socket")]
    UdpSocketError(#[from] std::io::Error),
    /// Simple-dns error related, usually packet parsing
    #[error("Failed to parse dns packet")]
    DnsParsing(#[from] SimpleDnsError),
}

fn create_socket(addr: &SocketAddr) -> io::Result<Socket> {
    let domain = if addr.is_ipv4() {
        Domain::IPV4
    } else {
        Domain::IPV6
    };

    let socket = Socket::new(domain, Type::DGRAM, Some(Protocol::UDP))?;
    socket.set_read_timeout(Some(Duration::from_millis(100)))?;
    socket.set_reuse_address(true)?;

    #[cfg(not(windows))]
    socket.set_reuse_port(true)?;

    Ok(socket)
}

fn join_multicast(addr: &SocketAddr) -> io::Result<UdpSocket> {
    let ip_addr = addr.ip();

    let socket = create_socket(addr)?;

    // depending on the IP protocol we have slightly different work
    match ip_addr {
        IpAddr::V4(ref mdns_v4) => {
            socket.join_multicast_v4(mdns_v4, &Ipv4Addr::UNSPECIFIED)?;
        }
        IpAddr::V6(ref mdns_v6) => {
            socket.join_multicast_v6(mdns_v6, 0)?;
            socket.set_only_v6(true)?;
        }
    };

    let socket = bind_multicast(socket, addr)?;
    Ok(socket.into())
}

#[cfg(unix)]
fn bind_multicast(socket: Socket, addr: &SocketAddr) -> io::Result<Socket> {
    socket.bind(&SockAddr::from(*addr))?;
    Ok(socket)
}

#[cfg(windows)]
fn bind_multicast(socket: Socket, addr: &SocketAddr) -> io::Result<Socket> {
    let addr = match addr {
        SocketAddr::V4(addr) => {
            SockAddr::from(SocketAddr::new(Ipv4Addr::UNSPECIFIED.into(), addr.port()))
        }
        SocketAddr::V6(addr) => {
            SockAddr::from(SocketAddr::new(Ipv6Addr::UNSPECIFIED.into(), addr.port()))
        }
    };

    socket.bind(&addr)?;
    Ok(socket)
}

fn sender_socket(addr: &SocketAddr) -> io::Result<UdpSocket> {
    let socket = create_socket(addr)?;
    if addr.is_ipv4() {
        socket.bind(&SockAddr::from(SocketAddr::new(
            Ipv4Addr::UNSPECIFIED.into(),
            0,
        )))?;
    } else {
        socket.bind(&SockAddr::from(SocketAddr::new(
            Ipv6Addr::UNSPECIFIED.into(),
            0,
        )))?;
    }
    Ok(socket.into())
}