Reuse some libraries that we already use in upnp crate
This commit is contained in:
parent
291b440f81
commit
01499231ac
3 changed files with 29 additions and 17 deletions
2
Cargo.lock
generated
2
Cargo.lock
generated
|
|
@ -1695,7 +1695,9 @@ version = "0.1.1"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"anyhow",
|
"anyhow",
|
||||||
"async-recursion",
|
"async-recursion",
|
||||||
|
"bstr",
|
||||||
"futures",
|
"futures",
|
||||||
|
"httparse",
|
||||||
"network-interface",
|
"network-interface",
|
||||||
"reqwest",
|
"reqwest",
|
||||||
"serde",
|
"serde",
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ futures = "0.3"
|
||||||
url = "2"
|
url = "2"
|
||||||
async-recursion = "1"
|
async-recursion = "1"
|
||||||
network-interface = { git = 'https://github.com/ikatson/network-interface', branch = "compile-on-freebsd" }
|
network-interface = { git = 'https://github.com/ikatson/network-interface', branch = "compile-on-freebsd" }
|
||||||
|
httparse = "1.9.4"
|
||||||
|
bstr = "1.10.0"
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
tokio = { version = "1", features = ["macros", "rt-multi-thread"] }
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,12 @@
|
||||||
use anyhow::{bail, Context};
|
use anyhow::{bail, Context};
|
||||||
|
use bstr::BStr;
|
||||||
use futures::{stream::FuturesUnordered, StreamExt, TryFutureExt};
|
use futures::{stream::FuturesUnordered, StreamExt, TryFutureExt};
|
||||||
use network_interface::NetworkInterfaceConfig;
|
use network_interface::NetworkInterfaceConfig;
|
||||||
use reqwest::Client;
|
use reqwest::Client;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use serde_xml_rs::from_str;
|
use serde_xml_rs::from_str;
|
||||||
use std::{
|
use std::{
|
||||||
collections::{HashMap, HashSet},
|
collections::HashSet,
|
||||||
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
net::{Ipv4Addr, SocketAddr, SocketAddrV4},
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
@ -20,7 +21,7 @@ const SSDP_SEARCH_REQUEST: &str = "M-SEARCH * HTTP/1.1\r\n\
|
||||||
Host: 239.255.255.250:1900\r\n\
|
Host: 239.255.255.250:1900\r\n\
|
||||||
Man: \"ssdp:discover\"\r\n\
|
Man: \"ssdp:discover\"\r\n\
|
||||||
MX: 3\r\n\
|
MX: 3\r\n\
|
||||||
ST: upnp:rootdevice\r\n\
|
ST: urn:schemas-upnp-org:service:WANIPConnection:1\r\n\
|
||||||
\r\n";
|
\r\n";
|
||||||
|
|
||||||
fn get_local_ip_relative_to(local_dest: Ipv4Addr) -> anyhow::Result<Ipv4Addr> {
|
fn get_local_ip_relative_to(local_dest: Ipv4Addr) -> anyhow::Result<Ipv4Addr> {
|
||||||
|
|
@ -265,16 +266,30 @@ async fn discover_services(location: Url) -> anyhow::Result<RootDesc> {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_upnp_discover_response(
|
fn parse_upnp_discover_response(
|
||||||
response: &str,
|
buf: &[u8],
|
||||||
received_from: SocketAddr,
|
received_from: SocketAddr,
|
||||||
) -> anyhow::Result<UpnpDiscoverResponse> {
|
) -> anyhow::Result<UpnpDiscoverResponse> {
|
||||||
let mut headers = HashMap::new();
|
let mut headers = [httparse::EMPTY_HEADER; 16];
|
||||||
for line in response.lines() {
|
let mut resp = httparse::Response::new(&mut headers);
|
||||||
if let Some((key, value)) = line.split_once(": ") {
|
resp.parse(buf).context("error parsing response")?;
|
||||||
headers.insert(key.to_lowercase(), value.trim_end().to_string());
|
|
||||||
|
trace!(?resp, "parsed SSDP response");
|
||||||
|
match resp.code {
|
||||||
|
Some(200) => {}
|
||||||
|
other => anyhow::bail!("bad response code {other:?}, expected 200"),
|
||||||
|
}
|
||||||
|
let mut location = None;
|
||||||
|
for header in resp.headers {
|
||||||
|
match header.name {
|
||||||
|
"location" | "LOCATION" | "Location" => {
|
||||||
|
location = Some(
|
||||||
|
std::str::from_utf8(header.value).context("bad utf-8 in location header")?,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
_ => continue,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let location = headers.get("location").context("missing location header")?;
|
let location = location.context("missing location header")?;
|
||||||
let location =
|
let location =
|
||||||
Url::parse(location).with_context(|| format!("failed parsing location {location}"))?;
|
Url::parse(location).with_context(|| format!("failed parsing location {location}"))?;
|
||||||
Ok(UpnpDiscoverResponse {
|
Ok(UpnpDiscoverResponse {
|
||||||
|
|
@ -352,20 +367,13 @@ impl UpnpPortForwarder {
|
||||||
timed_out = true;
|
timed_out = true;
|
||||||
}
|
}
|
||||||
Ok((len, addr)) = socket.recv_from(&mut buffer), if !timed_out => {
|
Ok((len, addr)) = socket.recv_from(&mut buffer), if !timed_out => {
|
||||||
let response = match std::str::from_utf8(&buffer[..len]) {
|
let response = &buffer[..len];
|
||||||
Ok(response) => response,
|
|
||||||
Err(_) => {
|
|
||||||
warn!(%addr, "received invalid utf-8");
|
|
||||||
continue;
|
|
||||||
},
|
|
||||||
};
|
|
||||||
trace!(%addr, response, "response");
|
|
||||||
match parse_upnp_discover_response(response, addr) {
|
match parse_upnp_discover_response(response, addr) {
|
||||||
Ok(r) => {
|
Ok(r) => {
|
||||||
tx.send(r)?;
|
tx.send(r)?;
|
||||||
discovered += 1;
|
discovered += 1;
|
||||||
},
|
},
|
||||||
Err(e) => warn!("failed to parse response: {e:#}"),
|
Err(e) => warn!(error=?e, response=?BStr::new(response), "failed to parse SSDP response"),
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue