use std::borrow::Cow;
use std::fmt::Debug;
use std::net::{ToSocketAddrs, SocketAddr, SocketAddrV6, IpAddr};
use std::str::FromStr;
use std::time::Duration;
use std::io;
use hyper::header::{Header, HeaderFormat};
use error::{SSDPResult, MsgError};
use header::{HeaderRef, HeaderMut, MX};
use message::{self, MessageType};
use message::ssdp::SSDPMessage;
use receiver::{SSDPReceiver, FromRawSSDP};
use net;
const NETWORK_TIMEOUT_OVERHEAD: u8 = 1;
const DEFAULT_UNICAST_TIMEOUT: u8 = 1 + NETWORK_TIMEOUT_OVERHEAD;
#[derive(Debug, Clone)]
pub struct SearchRequest {
message: SSDPMessage,
}
impl SearchRequest {
pub fn new() -> SearchRequest {
SearchRequest { message: SSDPMessage::new(MessageType::Search) }
}
pub fn unicast<A: ToSocketAddrs>(&mut self, dst_addr: A) -> SSDPResult<SSDPReceiver<SearchResponse>> {
let mode = try!(net::IpVersionMode::from_addr(&dst_addr));
let mut connectors = try!(message::all_local_connectors(None, mode));
for connector in &mut connectors {
try!(self.message.send(connector, &dst_addr));
}
let mut raw_connectors = Vec::with_capacity(connectors.len());
raw_connectors.extend(connectors.into_iter().map(|conn| conn.deconstruct()));
let opt_timeout = opt_unicast_timeout(self.get::<MX>());
Ok(try!(SSDPReceiver::new(raw_connectors, opt_timeout)))
}
pub fn multicast(&mut self) -> SSDPResult<SSDPReceiver<SearchResponse>> {
self.multicast_with_port(message::UPNP_MULTICAST_PORT)
}
pub fn multicast_with_port(&mut self, port: u16) -> SSDPResult<SSDPReceiver<SearchResponse>> {
let mcast_timeout = try!(multicast_timeout(self.get::<MX>()));
let mcast_ttl = Some(message::UPNP_MULTICAST_TTL);
let mut connectors = try!(message::all_local_connectors(mcast_ttl, net::IpVersionMode::Any));
for conn in &mut connectors {
match try!(conn.local_addr()) {
SocketAddr::V4(_) => {
try!(self.message.send(conn, &(message::UPNP_MULTICAST_IPV4_ADDR, port)))
}
SocketAddr::V6(n) => {
try!(self.message.send(conn,
&SocketAddrV6::new(try!(
FromStr::from_str(message::UPNP_MULTICAST_IPV6_LINK_LOCAL_ADDR)),
port,
n.flowinfo(),
n.scope_id())))
}
}
}
let mut raw_connectors = Vec::with_capacity(connectors.len());
raw_connectors.extend(connectors.into_iter().map(|conn| conn.deconstruct()));
Ok(try!(SSDPReceiver::new(raw_connectors, Some(mcast_timeout))))
}
}
impl Default for SearchRequest {
fn default() -> Self {
SearchRequest::new()
}
}
fn multicast_timeout(mx: Option<&MX>) -> SSDPResult<Duration> {
match mx {
Some(&MX(n)) => Ok(Duration::new((n + NETWORK_TIMEOUT_OVERHEAD) as u64, 0)),
None => try!(Err(MsgError::new("Multicast Searches Require An MX Header"))),
}
}
fn opt_unicast_timeout(mx: Option<&MX>) -> Option<Duration> {
match mx {
Some(&MX(n)) => Some(Duration::new((n + NETWORK_TIMEOUT_OVERHEAD) as u64, 0)),
None => Some(Duration::new(DEFAULT_UNICAST_TIMEOUT as u64, 0)),
}
}
impl FromRawSSDP for SearchRequest {
fn raw_ssdp(bytes: &[u8]) -> SSDPResult<SearchRequest> {
let message = try!(SSDPMessage::raw_ssdp(bytes));
if message.message_type() != MessageType::Search {
try!(Err(MsgError::new("SSDP Message Received Is Not A SearchRequest")))
} else {
Ok(SearchRequest { message: message })
}
}
}
impl HeaderRef for SearchRequest {
fn get<H>(&self) -> Option<&H>
where H: Header + HeaderFormat
{
self.message.get::<H>()
}
fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
self.message.get_raw(name)
}
}
impl HeaderMut for SearchRequest {
fn set<H>(&mut self, value: H)
where H: Header + HeaderFormat
{
self.message.set(value)
}
fn set_raw<K>(&mut self, name: K, value: Vec<Vec<u8>>)
where K: Into<Cow<'static, str>> + Debug
{
self.message.set_raw(name, value)
}
}
#[derive(Debug, Clone)]
pub struct SearchResponse {
message: SSDPMessage,
}
impl SearchResponse {
pub fn new() -> SearchResponse {
SearchResponse { message: SSDPMessage::new(MessageType::Response) }
}
pub fn unicast<A: ToSocketAddrs>(&mut self, dst_addr: A) -> SSDPResult<()> {
let mode = try!(net::IpVersionMode::from_addr(&dst_addr));
let mut connectors = try!(message::all_local_connectors(None, mode));
let mut success_count = 0;
let mut error_count = 0;
for conn in &mut connectors {
match self.message.send(conn, &dst_addr) {
Ok(_) => success_count += 1,
Err(_) => error_count += 1,
}
}
if success_count == 0 && error_count > 0 {
try!(Err(io::Error::last_os_error()));
}
Ok(())
}
}
impl Default for SearchResponse {
fn default() -> Self {
SearchResponse::new()
}
}
pub struct SearchListener;
impl SearchListener {
pub fn listen() -> SSDPResult<SSDPReceiver<SearchRequest>> {
SearchListener::listen_on_port(message::UPNP_MULTICAST_PORT)
}
pub fn listen_on_port(port: u16) -> SSDPResult<SSDPReceiver<SearchRequest>> {
let mut ipv4_sock = None;
let mut ipv6_sock = None;
let addrs: Vec<SocketAddr> = try!(message::map_local(|&addr| Ok(Some(addr))));
for addr in addrs {
match addr {
SocketAddr::V4(_) => {
let mcast_ip = message::UPNP_MULTICAST_IPV4_ADDR.parse().unwrap();
if ipv4_sock.is_none() {
ipv4_sock = Some(try!(net::bind_reuse(("0.0.0.0", port))));
}
let ref sock = ipv4_sock.as_ref().unwrap();
debug!("Joining ipv4 multicast {} at iface: {}", mcast_ip, addr);
try!(net::join_multicast(&sock, &addr, &mcast_ip));
}
SocketAddr::V6(_) => {
let mcast_ip = message::UPNP_MULTICAST_IPV6_LINK_LOCAL_ADDR.parse().unwrap();
if ipv6_sock.is_none() {
ipv6_sock = Some(try!(net::bind_reuse(("::", port))));
}
let ref sock = ipv6_sock.as_ref().unwrap();
debug!("Joining ipv6 multicast {} at iface: {}", mcast_ip, addr);
try!(net::join_multicast(&sock, &addr, &IpAddr::V6(mcast_ip)));
}
}
}
let sockets = vec![ipv4_sock, ipv6_sock]
.into_iter()
.flat_map(|opt_interface| opt_interface)
.collect();
Ok(try!(SSDPReceiver::new(sockets, None)))
}
}
impl FromRawSSDP for SearchResponse {
fn raw_ssdp(bytes: &[u8]) -> SSDPResult<SearchResponse> {
let message = try!(SSDPMessage::raw_ssdp(bytes));
if message.message_type() != MessageType::Response {
try!(Err(MsgError::new("SSDP Message Received Is Not A SearchResponse")))
} else {
Ok(SearchResponse { message: message })
}
}
}
impl HeaderRef for SearchResponse {
fn get<H>(&self) -> Option<&H>
where H: Header + HeaderFormat
{
self.message.get::<H>()
}
fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
self.message.get_raw(name)
}
}
impl HeaderMut for SearchResponse {
fn set<H>(&mut self, value: H)
where H: Header + HeaderFormat
{
self.message.set(value)
}
fn set_raw<K>(&mut self, name: K, value: Vec<Vec<u8>>)
where K: Into<Cow<'static, str>> + Debug
{
self.message.set_raw(name, value)
}
}
#[cfg(test)]
mod tests {
use header::MX;
#[test]
fn positive_multicast_timeout() {
super::multicast_timeout(Some(&MX(5))).unwrap();
}
#[test]
fn positive_some_opt_multicast_timeout() {
super::opt_unicast_timeout(Some(&MX(5))).unwrap();
}
#[test]
fn positive_none_opt_multicast_timeout() {
super::opt_unicast_timeout(None).unwrap();
}
#[test]
#[should_panic]
fn negative_multicast_timeout() {
super::multicast_timeout(None).unwrap();
}
}