Nothing: renames and tiny refactors
This commit is contained in:
parent
8d8d5491f9
commit
028addcf6a
2 changed files with 42 additions and 27 deletions
|
|
@ -1,5 +1,5 @@
|
||||||
pub const UPNP_KIND_ROOT_DEVICE: &str = "upnp:rootdevice";
|
pub const UPNP_DEVICE_ROOT: &str = "upnp:rootdevice";
|
||||||
pub const UPNP_KIND_MEDIASERVER: &str = "urn:schemas-upnp-org:device:MediaServer:1";
|
pub const UPNP_DEVICE_MEDIASERVER: &str = "urn:schemas-upnp-org:device:MediaServer:1";
|
||||||
|
|
||||||
pub const SOAP_ACTION_CONTENT_DIRECTORY_BROWSE: &[u8] =
|
pub const SOAP_ACTION_CONTENT_DIRECTORY_BROWSE: &[u8] =
|
||||||
b"\"urn:schemas-upnp-org:service:ContentDirectory:1#Browse\"";
|
b"\"urn:schemas-upnp-org:service:ContentDirectory:1#Browse\"";
|
||||||
|
|
|
||||||
|
|
@ -12,7 +12,7 @@ use tokio::net::UdpSocket;
|
||||||
use tokio_util::sync::CancellationToken;
|
use tokio_util::sync::CancellationToken;
|
||||||
use tracing::{debug, trace, warn};
|
use tracing::{debug, trace, warn};
|
||||||
|
|
||||||
use crate::constants::{UPNP_KIND_MEDIASERVER, UPNP_KIND_ROOT_DEVICE};
|
use crate::constants::{UPNP_DEVICE_MEDIASERVER, UPNP_DEVICE_ROOT};
|
||||||
|
|
||||||
const SSDP_PORT: u16 = 1900;
|
const SSDP_PORT: u16 = 1900;
|
||||||
const SSDM_MCAST_IPV4: Ipv4Addr = Ipv4Addr::new(239, 255, 255, 250);
|
const SSDM_MCAST_IPV4: Ipv4Addr = Ipv4Addr::new(239, 255, 255, 250);
|
||||||
|
|
@ -23,8 +23,10 @@ const NTS_ALIVE: &str = "ssdp:alive";
|
||||||
const NTS_BYEBYE: &str = "ssdp:byebye";
|
const NTS_BYEBYE: &str = "ssdp:byebye";
|
||||||
|
|
||||||
fn ipv6_is_link_local(ip: Ipv6Addr) -> bool {
|
fn ipv6_is_link_local(ip: Ipv6Addr) -> bool {
|
||||||
let s = ip.segments();
|
const LL: Ipv6Addr = Ipv6Addr::new(0xfe80, 0, 0, 0, 0, 0, 0, 0);
|
||||||
[s[0], s[1], s[2], s[3]] == [0xfe80, 0, 0, 0]
|
const MASK: Ipv6Addr = Ipv6Addr::new(0xffff, 0xffff, 0xffff, 0xffff, 0, 0, 0, 0);
|
||||||
|
|
||||||
|
ip.to_bits() & MASK.to_bits() == LL.to_bits() & MASK.to_bits()
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
|
|
@ -49,7 +51,7 @@ impl<'a> SsdpMSearchRequest<'a> {
|
||||||
if self.man != "\"ssdp:discover\"" {
|
if self.man != "\"ssdp:discover\"" {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
if self.st == UPNP_KIND_ROOT_DEVICE || self.st == UPNP_KIND_MEDIASERVER {
|
if self.st == UPNP_DEVICE_ROOT || self.st == UPNP_DEVICE_MEDIASERVER {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
false
|
false
|
||||||
|
|
@ -200,6 +202,7 @@ async fn bind_v6_socket() -> anyhow::Result<UdpSocket> {
|
||||||
if !present {
|
if !present {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
trace!(multiaddr=?multiaddr, interface=?nic.index, "joining multicast v6 group");
|
||||||
if let Err(e) = socket.join_multicast_v6(&multiaddr, nic.index) {
|
if let Err(e) = socket.join_multicast_v6(&multiaddr, nic.index) {
|
||||||
debug!(multiaddr=?multiaddr, interface=?nic.index, "error joining multicast v6 group: {e:#}");
|
debug!(multiaddr=?multiaddr, interface=?nic.index, "error joining multicast v6 group: {e:#}");
|
||||||
}
|
}
|
||||||
|
|
@ -210,10 +213,10 @@ async fn bind_v6_socket() -> anyhow::Result<UdpSocket> {
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MulticastOpts {
|
struct MulticastOpts {
|
||||||
local_interface_ip: IpAddr,
|
interface_addr: IpAddr,
|
||||||
#[allow(dead_code)]
|
#[allow(dead_code)]
|
||||||
local_interface_id: u32,
|
interface_id: u32,
|
||||||
addr: SocketAddr,
|
mcast_addr: SocketAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_mcast_if(sock: &UdpSocket, local_ip: Ipv4Addr) -> anyhow::Result<()> {
|
fn set_mcast_if(sock: &UdpSocket, local_ip: Ipv4Addr) -> anyhow::Result<()> {
|
||||||
|
|
@ -259,7 +262,7 @@ fn set_mcast_if(sock: &UdpSocket, local_ip: Ipv4Addr) -> anyhow::Result<()> {
|
||||||
|
|
||||||
impl MulticastOpts {
|
impl MulticastOpts {
|
||||||
fn addr_no_scope(&self) -> SocketAddr {
|
fn addr_no_scope(&self) -> SocketAddr {
|
||||||
let mut addr = self.addr;
|
let mut addr = self.mcast_addr;
|
||||||
if let SocketAddr::V6(v6) = &mut addr {
|
if let SocketAddr::V6(v6) = &mut addr {
|
||||||
v6.set_scope_id(0);
|
v6.set_scope_id(0);
|
||||||
}
|
}
|
||||||
|
|
@ -284,21 +287,26 @@ impl SsdpRunner {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generate_notify_message(&self, kind: &str, nts: &str, opts: &MulticastOpts) -> String {
|
fn generate_notify_message(
|
||||||
|
&self,
|
||||||
|
device_kind: &str,
|
||||||
|
nts: &str,
|
||||||
|
opts: &MulticastOpts,
|
||||||
|
) -> String {
|
||||||
let usn: &str = &self.opts.usn;
|
let usn: &str = &self.opts.usn;
|
||||||
let server: &str = &self.opts.server_string;
|
let server: &str = &self.opts.server_string;
|
||||||
let host = opts.addr_no_scope();
|
let host = opts.addr_no_scope();
|
||||||
let mut location = self.opts.description_http_location.clone();
|
let mut location = self.opts.description_http_location.clone();
|
||||||
let _ = location.set_ip_host(opts.local_interface_ip);
|
let _ = location.set_ip_host(opts.interface_addr);
|
||||||
format!(
|
format!(
|
||||||
"NOTIFY * HTTP/1.1\r
|
"NOTIFY * HTTP/1.1\r
|
||||||
Host: {host}\r
|
Host: {host}\r
|
||||||
Cache-Control: max-age=75\r
|
Cache-Control: max-age=75\r
|
||||||
Location: {location}\r
|
Location: {location}\r
|
||||||
NT: {kind}\r
|
NT: {device_kind}\r
|
||||||
NTS: {nts}\r
|
NTS: {nts}\r
|
||||||
Server: {server}\r
|
Server: {server}\r
|
||||||
USN: {usn}::{kind}\r
|
USN: {usn}::{device_kind}\r
|
||||||
\r
|
\r
|
||||||
"
|
"
|
||||||
)
|
)
|
||||||
|
|
@ -352,15 +360,15 @@ Content-Length: 0\r\n\r\n"
|
||||||
.filter_map(|(ifidx, addr)| match addr.ip() {
|
.filter_map(|(ifidx, addr)| match addr.ip() {
|
||||||
std::net::IpAddr::V4(a) if !a.is_loopback() && a.is_private() => {
|
std::net::IpAddr::V4(a) if !a.is_loopback() && a.is_private() => {
|
||||||
Some(MulticastOpts {
|
Some(MulticastOpts {
|
||||||
local_interface_ip: addr.ip(),
|
interface_addr: addr.ip(),
|
||||||
local_interface_id: ifidx,
|
interface_id: ifidx,
|
||||||
addr: SocketAddr::V4(SocketAddrV4::new(SSDM_MCAST_IPV4, SSDP_PORT)),
|
mcast_addr: SocketAddr::V4(SocketAddrV4::new(SSDM_MCAST_IPV4, SSDP_PORT)),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
std::net::IpAddr::V6(a) if !a.is_loopback() => Some(MulticastOpts {
|
std::net::IpAddr::V6(a) if !a.is_loopback() => Some(MulticastOpts {
|
||||||
local_interface_ip: addr.ip(),
|
interface_addr: addr.ip(),
|
||||||
local_interface_id: ifidx,
|
interface_id: ifidx,
|
||||||
addr: {
|
mcast_addr: {
|
||||||
let bip = if ipv6_is_link_local(a) {
|
let bip = if ipv6_is_link_local(a) {
|
||||||
SSDP_MCAST_IPV6_LINK_LOCAL
|
SSDP_MCAST_IPV6_LINK_LOCAL
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -375,20 +383,27 @@ Content-Length: 0\r\n\r\n"
|
||||||
let payload = get_payload(&opts);
|
let payload = get_payload(&opts);
|
||||||
if !sent
|
if !sent
|
||||||
.lock()
|
.lock()
|
||||||
.insert((payload.clone(), opts.local_interface_id, opts.addr))
|
.insert((payload.clone(), opts.interface_id, opts.mcast_addr))
|
||||||
{
|
{
|
||||||
// don't send duplicates
|
// don't send duplicates
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
let sock = match (
|
let sock = match (
|
||||||
opts.local_interface_ip,
|
opts.interface_addr,
|
||||||
self.socket_v4.as_ref(),
|
self.socket_v4.as_ref(),
|
||||||
self.socket_v6.as_ref(),
|
self.socket_v6.as_ref(),
|
||||||
) {
|
) {
|
||||||
|
// For IPv4 sockets, call setsockopt(IP_MULTICAST_IF), so that the message
|
||||||
|
// gets sent out of the interface we want (otherwise it'll get sent through
|
||||||
|
// default one).
|
||||||
|
// For IPv6 it's not necessary as we specify scope_id in SocketAddr.
|
||||||
|
//
|
||||||
|
// It's important we don't .await() in between also, so that concurrent sends
|
||||||
|
// have the proper IP_MULTICAST_IF.
|
||||||
(IpAddr::V4(ip), Some(sock_v4), _) => {
|
(IpAddr::V4(ip), Some(sock_v4), _) => {
|
||||||
if let Err(e) = set_mcast_if(sock_v4, ip) {
|
if let Err(e) = set_mcast_if(sock_v4, ip) {
|
||||||
debug!(addr=%ip, "error calling set_mcast_if: {e:#}");
|
debug!(addr=%ip, "error setting IP_MULTICAST_IF: {e:#}");
|
||||||
}
|
}
|
||||||
sock_v4
|
sock_v4
|
||||||
}
|
}
|
||||||
|
|
@ -396,10 +411,10 @@ Content-Length: 0\r\n\r\n"
|
||||||
_ => return,
|
_ => return,
|
||||||
};
|
};
|
||||||
|
|
||||||
match sock.send_to(payload.as_slice(), opts.addr).await {
|
match sock.send_to(payload.as_slice(), opts.mcast_addr).await {
|
||||||
Ok(sz) => trace!(payload=?payload, addr=%opts.addr, size=sz, "sent"),
|
Ok(sz) => trace!(payload=?payload, addr=%opts.mcast_addr, size=sz, "sent"),
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
debug!(payload=?payload, addr=%opts.addr, "error sending: {e:#}")
|
debug!(payload=?payload, addr=%opts.mcast_addr, "error sending: {e:#}")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
@ -409,7 +424,7 @@ Content-Length: 0\r\n\r\n"
|
||||||
|
|
||||||
async fn try_send_notifies(&self, nts: &str) {
|
async fn try_send_notifies(&self, nts: &str) {
|
||||||
self.try_send_mcast_everywhere(&|opts| {
|
self.try_send_mcast_everywhere(&|opts| {
|
||||||
self.generate_notify_message(UPNP_KIND_MEDIASERVER, nts, opts)
|
self.generate_notify_message(UPNP_DEVICE_MEDIASERVER, nts, opts)
|
||||||
.into()
|
.into()
|
||||||
})
|
})
|
||||||
.await
|
.await
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue