#![cfg(not(all(target_arch = "wasm32", target_os = "unknown")))]
pub(crate) mod gateway;
mod http;
mod messages;
pub(crate) mod search;
pub(crate) mod xml;
pub use gateway::Gateway;
pub use search::search_gateway;
use std::collections::HashMap;
use std::fmt;
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, UdpSocket};
use std::time::Duration;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
#[allow(clippy::upper_case_acronyms)]
pub enum PortMappingProtocol {
TCP,
UDP,
}
impl fmt::Display for PortMappingProtocol {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
PortMappingProtocol::TCP => write!(f, "TCP"),
PortMappingProtocol::UDP => write!(f, "UDP"),
}
}
}
pub enum Ipv6SearchScope {
LinkLocal,
Subnet,
Admin,
SiteLocal,
}
pub struct SearchOptions {
pub bind_addr: SocketAddr,
pub broadcast_address: SocketAddr,
pub timeout: Option<Duration>,
}
impl Default for SearchOptions {
fn default() -> Self {
Self::new_v4(10_000)
}
}
impl SearchOptions {
#[must_use]
pub fn new_v4(timeout_ms: u64) -> Self {
Self {
bind_addr: (IpAddr::V4(Ipv4Addr::UNSPECIFIED), 0).into(),
broadcast_address: "239.255.255.250:1900".parse().unwrap(),
timeout: Some(Duration::from_millis(timeout_ms)),
}
}
#[must_use]
pub fn new_v6(scope: Ipv6SearchScope, timeout_ms: u64) -> Self {
Self {
bind_addr: (IpAddr::V6(Ipv6Addr::UNSPECIFIED), 0).into(),
broadcast_address: match scope {
Ipv6SearchScope::LinkLocal => "[FF02::C]:1900",
Ipv6SearchScope::Subnet => "[FF03::C]:1900",
Ipv6SearchScope::Admin => "[FF04::C]:1900",
Ipv6SearchScope::SiteLocal => "[FF05::C]:1900",
}
.parse()
.unwrap(),
timeout: Some(Duration::from_millis(timeout_ms)),
}
}
}
#[derive(Debug, Clone)]
pub struct IgdError {
soap_code: Option<u16>,
message: String,
}
impl IgdError {
fn message(message: impl Into<String>) -> Self {
Self {
soap_code: None,
message: message.into(),
}
}
fn parse(message: impl Into<String>) -> Self {
Self {
soap_code: None,
message: format!("parse error: {}", message.into()),
}
}
fn http(message: impl Into<String>) -> Self {
Self {
soap_code: None,
message: format!("http error: {}", message.into()),
}
}
fn soap(code: u16, description: String) -> Self {
Self {
soap_code: Some(code),
message: format!("upnp error {}: {}", code, description),
}
}
fn soap_code(&self) -> Option<u16> {
self.soap_code
}
}
impl fmt::Display for IgdError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.message)
}
}
impl std::error::Error for IgdError {}