Skip to content

Commit 318fc70

Browse files
committed
Merge branch 'main' into fix/additionals
2 parents 85ca3e6 + 352c930 commit 318fc70

File tree

20 files changed

+438
-248
lines changed

20 files changed

+438
-248
lines changed

.github/workflows/rust.yml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,4 +14,6 @@ jobs:
1414
steps:
1515
- uses: actions/checkout@v2
1616
- name: Check formatting
17-
run: cargo fmt -- --check
17+
run: cargo +stable fmt -- --check
18+
- name: Check linting
19+
run: cargo +stable clippy -- -Dwarnings

.github/workflows/zeroconf_test.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
- name: Install our MSRV
1414
uses: dtolnay/rust-toolchain@stable
1515
with:
16-
toolchain: "1.70"
16+
toolchain: "1.74"
1717

1818
- name: Compile example
1919
run: cargo build --example register

Cargo.toml

Lines changed: 18 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,35 +1,33 @@
11
[package]
22
name = "libmdns"
3-
version = "0.8.0"
3+
version = "0.9.1"
44
authors = ["Will Stott <willstott101+libmdns@gmail.com>"]
5-
65
description = "mDNS Responder library for building discoverable LAN services in Rust"
76
repository = "https://github.com/librespot-org/libmdns"
87
readme = "README.md"
98
license = "MIT"
109
edition = "2018"
10+
rust-version = "1.74"
1111

1212
[dependencies]
1313
byteorder = "1.5"
14-
if-addrs = "0.11.0"
15-
hostname = "0.3.1"
16-
log = "0.4"
17-
multimap = "0.9"
18-
rand = "0.8"
1914
futures-util = "0.3"
20-
thiserror = "1.0"
21-
tokio = { version = "1.0", features = ["sync", "net", "rt"] }
22-
socket2 = { version = "0.5", features = ["all"] }
23-
24-
[target.'cfg(windows)'.dependencies]
25-
winapi = { version = "0.3", features = ["netioapi"] }
26-
27-
[target.'cfg(not(windows))'.dependencies]
28-
nix = { version = "0.27", features = ["net"] }
15+
hostname = "0.4"
16+
if-addrs = { version = "0.14", features = ["link-local"] }
17+
log = "0.4"
18+
multimap = { version = "0.10", default-features = false }
19+
rand = "0.9"
20+
socket2 = { version = "0.6", features = ["all"] }
21+
thiserror = "2"
22+
tokio = { version = "1", default-features = false, features = [
23+
"sync",
24+
"net",
25+
"rt",
26+
] }
2927

3028
[dev-dependencies]
31-
env_logger = { version = "0.10", default-features = false, features = [
32-
"color",
33-
"humantime",
34-
"auto-color",
29+
env_logger = { version = "0.11", default-features = false, features = [
30+
"color",
31+
"humantime",
32+
"auto-color",
3533
] }

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,14 +13,14 @@ To use it, add this to your `Cargo.toml`:
1313

1414
```toml
1515
[dependencies]
16-
libmdns = "0.7"
16+
libmdns = "0.9"
1717
```
1818

19-
See the [example](https://github.com/librespot-org/libmdns/blob/stable-0.7.x/examples/register.rs) for use within code.
19+
See the [example](https://github.com/librespot-org/libmdns/blob/stable-0.9.x/examples/register.rs) for use within code.
2020

2121
## Dependencies
2222

23-
libmdns' oldest supported Rust toolchain is `1.70.0`, _however it may compile fine on older versions of rust._
23+
libmdns' MSRV (oldest supported Rust toolchain) is 1.74.0.
2424

2525
**We hold no strong garantees for sticking to a Minimum Supported Rust Version**. Please open an issue on GitHub if you need support for older compilers or different platforms.
2626

examples/register.rs

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,8 @@ pub fn main() {
33
builder.parse_filters("libmdns=debug");
44
builder.init();
55

6-
let responder = libmdns::Responder::new().unwrap();
7-
let _svc = responder.register(
8-
"_http._tcp".to_owned(),
9-
"libmdns Web Server".to_owned(),
10-
80,
11-
&["path=/"],
12-
);
6+
let responder = libmdns::Responder::new();
7+
let _svc = responder.register("_http._tcp", "libmdns Web Server", 80, &["path=/"]);
138

149
loop {
1510
::std::thread::sleep(::std::time::Duration::from_secs(10));

examples/register_with_ip_list.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,7 @@ pub fn main() {
1010
];
1111

1212
let responder = libmdns::Responder::new_with_ip_list(vec).unwrap();
13-
let _svc = responder.register(
14-
"_http._tcp".to_owned(),
15-
"libmdns Web Server".to_owned(),
16-
80,
17-
&["path=/"],
18-
);
13+
let _svc = responder.register("_http._tcp", "libmdns Web Server", 80, &["path=/"]);
1914

2015
loop {
2116
::std::thread::sleep(::std::time::Duration::from_secs(10));

examples/zeroconf_test.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
1-
from zeroconf import ServiceBrowser, Zeroconf, IPVersion, ZeroconfServiceTypes
1+
from zeroconf import ServiceBrowser, ServiceListener, Zeroconf, IPVersion, ZeroconfServiceTypes
22
from time import sleep
33

44

55
TYPE = "_http._tcp.local."
66
NAME = "libmdns Web Server"
77

88

9-
class MyListener:
9+
class MyListener(ServiceListener):
1010
def __init__(self):
1111
self.found = []
1212

src/address_family.rs

Lines changed: 53 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,10 @@
11
use super::MDNS_PORT;
2-
use if_addrs::get_if_addrs;
2+
use if_addrs::{get_if_addrs, IfAddr};
33
use socket2::{Domain, Protocol, SockAddr, Socket, Type};
4+
use std::collections::HashSet;
45
use std::io;
56
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket};
67

7-
#[cfg(not(windows))]
8-
use nix::net::if_::if_nametoindex;
9-
10-
#[cfg(windows)]
11-
use win::if_nametoindex;
12-
138
pub enum Inet {}
149

1510
pub enum Inet6 {}
@@ -34,8 +29,7 @@ pub trait AddressFamily {
3429
socket.set_reuse_address(true)?;
3530
socket.set_nonblocking(true)?;
3631

37-
#[cfg(not(windows))]
38-
#[cfg(not(target_os = "illumos"))]
32+
#[cfg(all(unix, not(any(target_os = "solaris", target_os = "illumos"))))]
3933
socket.set_reuse_port(true)?;
4034

4135
socket.bind(&addr)?;
@@ -53,14 +47,13 @@ impl AddressFamily for Inet {
5347
const DOMAIN: Domain = Domain::IPV4;
5448

5549
fn join_multicast(socket: &Socket, multiaddr: &Self::Addr) -> io::Result<()> {
56-
let addresses = get_address_list()?;
57-
if addresses.is_empty() {
50+
let addrs = get_one_nonloopback_ipv4_addr_per_iface()?;
51+
if addrs.is_empty() {
5852
socket.join_multicast_v4(multiaddr, &Ipv4Addr::UNSPECIFIED)
5953
} else {
60-
for (_, address) in addresses {
61-
if let IpAddr::V4(ip) = address {
62-
socket.join_multicast_v4(multiaddr, &ip)?;
63-
}
54+
// TODO: If any join succeeds return success (log failures)
55+
for ip in addrs {
56+
socket.join_multicast_v4(multiaddr, &ip)?;
6457
}
6558
Ok(())
6659
}
@@ -76,44 +69,61 @@ impl AddressFamily for Inet6 {
7669
const DOMAIN: Domain = Domain::IPV6;
7770

7871
fn join_multicast(socket: &Socket, multiaddr: &Self::Addr) -> io::Result<()> {
79-
let addresses = get_address_list()?;
80-
if addresses.is_empty() {
72+
let indexes = get_one_nonloopback_ipv6_index_per_iface()?;
73+
if indexes.is_empty() {
8174
socket.join_multicast_v6(multiaddr, 0)
8275
} else {
83-
// We join multicast by interface, but each interface can have more than one ipv6 address.
84-
// So we have to check we're not registering more than once, as the resulting error is then
85-
// fatal to ipv6 listening.
86-
// TODO: Make each interface resilient to failures on another.
87-
let mut registered = Vec::new();
88-
for (iface_name, address) in addresses {
89-
if let IpAddr::V6(_) = address {
90-
let ipv6_index = if_nametoindex(iface_name.as_str()).unwrap_or(0);
91-
if ipv6_index != 0 && !registered.contains(&ipv6_index) {
92-
socket.join_multicast_v6(multiaddr, ipv6_index)?;
93-
registered.push(ipv6_index);
94-
}
95-
}
76+
// TODO: If any join succeeds return success (log failures)
77+
for ipv6_index in indexes {
78+
socket.join_multicast_v6(multiaddr, ipv6_index)?;
9679
}
9780
Ok(())
9881
}
9982
}
10083
}
10184

102-
fn get_address_list() -> io::Result<Vec<(String, IpAddr)>> {
85+
fn get_one_nonloopback_ipv6_index_per_iface() -> io::Result<Vec<u32>> {
86+
// There may be multiple ip addresses on a single interface and we join multicast by interface.
87+
// Joining multicast on the same interface multiple times returns an error
88+
// so we filter duplicate interfaces.
89+
let mut collected_interfaces = HashSet::new();
10390
Ok(get_if_addrs()?
104-
.iter()
105-
.filter(|iface| !iface.is_loopback())
106-
.map(|iface| (iface.name.clone(), iface.ip()))
91+
.into_iter()
92+
.filter_map(|iface| {
93+
if iface.is_loopback() {
94+
None
95+
} else if matches!(iface.addr, IfAddr::V6(_)) {
96+
if collected_interfaces.insert(iface.name.clone()) {
97+
iface.index
98+
} else {
99+
None
100+
}
101+
} else {
102+
None
103+
}
104+
})
107105
.collect())
108106
}
109107

110-
#[cfg(windows)]
111-
mod win {
112-
use std::ffi::{CString, NulError};
113-
use winapi::shared::netioapi;
114-
115-
pub fn if_nametoindex(ifname: &str) -> Result<u32, NulError> {
116-
let c_str = CString::new(ifname)?;
117-
Ok(unsafe { netioapi::if_nametoindex(c_str.as_ptr()) })
118-
}
108+
fn get_one_nonloopback_ipv4_addr_per_iface() -> io::Result<Vec<Ipv4Addr>> {
109+
// There may be multiple ip addresses on a single interface and we join multicast by interface.
110+
// Joining multicast on the same interface multiple times returns an error
111+
// so we filter duplicate interfaces.
112+
let mut collected_interfaces = HashSet::new();
113+
Ok(get_if_addrs()?
114+
.into_iter()
115+
.filter_map(|iface| {
116+
if iface.is_loopback() {
117+
None
118+
} else if let IpAddr::V4(ip) = iface.ip() {
119+
if collected_interfaces.insert(iface.name.clone()) {
120+
Some(ip)
121+
} else {
122+
None
123+
}
124+
} else {
125+
None
126+
}
127+
})
128+
.collect())
119129
}

0 commit comments

Comments
 (0)