Skip to content
This repository has been archived by the owner on Dec 8, 2023. It is now read-only.

Commit

Permalink
Merge pull request #40 from CDuPlooy/patch-1
Browse files Browse the repository at this point in the history
Ability to pick upnp control url
  • Loading branch information
sbstp committed Jul 30, 2019
2 parents f2e46a5 + b31fd89 commit 72dec06
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 20 deletions.
8 changes: 4 additions & 4 deletions src/aio/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ impl SearchFuture {
}

// Process a control response to extract the control URL
fn handle_control_resp(addr: SocketAddr, resp: Bytes) -> Result<String, SearchError> {
fn handle_control_resp(addr: SocketAddr, resp: Bytes) -> Result<Vec<String>, SearchError> {
debug!("handling control response from: {}", addr);

// Create a cursor over the response data
Expand Down Expand Up @@ -145,12 +145,12 @@ impl Future for SearchFuture {

// Handle any responses
if let Ok(url) = Self::handle_control_resp(*addr, resp) {
debug!("received control url from: {} (url: {})", addr, url);
*state = SearchState::Done(url.clone());
debug!("received control url from: {} (url: {:?})", addr, url);
*state = SearchState::Done(url[0].clone());

match addr {
SocketAddr::V4(a) => {
let g = Gateway::new(*a, url);
let g = Gateway::new(*a, url[0].clone());
return Ok(Async::Ready(g));
}
_ => warn!("unsupported IPv6 gateway response from addr: {}", addr),
Expand Down
21 changes: 10 additions & 11 deletions src/common/parsing.rs
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ pub fn parse_search_result(text: &str) -> Result<(SocketAddrV4, String), SearchE
Err(InvalidResponse)
}

pub fn parse_control_url<R>(resp: R) -> Result<String, SearchError>
pub fn parse_control_url<R>(resp: R) -> Result<Vec<String>, SearchError>
where
R: io::Read,
{
Expand All @@ -43,18 +43,17 @@ where
return Err(SearchError::InvalidResponse);
}

fn parse_control_url_scan_device(device: &Element) -> Result<String, SearchError> {
fn parse_control_url_scan_device(device: &Element) -> Result<Vec<String>, SearchError> {
let service_list = device.get_child("serviceList").ok_or(SearchError::InvalidResponse)?;
let mut urls: Vec<String> = Vec::new();
for service in &service_list.children {
if service.name == "service" {
if let Some(service_type) = service.get_child("serviceType") {
if service_type.text.as_ref().map(|s| s.as_str())
== Some("urn:schemas-upnp-org:service:WANPPPConnection:1") || service_type.text.as_ref().map(|s| s.as_str())
== Some("urn:schemas-upnp-org:service:WANIPConnection:1")
if service_type.text.as_ref().map(|s| s.as_str()).unwrap().contains("Connection")
{
if let Some(control_url) = service.get_child("controlURL") {
if let Some(text) = &control_url.text {
return Ok(text.clone());
return Ok(vec![text.clone()]);
}
}
}
Expand All @@ -66,12 +65,12 @@ fn parse_control_url_scan_device(device: &Element) -> Result<String, SearchError
for sub_device in &device_list.children {
if sub_device.name == "device" {
if let Ok(control_url) = parse_control_url_scan_device(&sub_device) {
return Ok(control_url);
urls.extend(control_url); // Need to look at this some more.
}
}
}

return Err(SearchError::InvalidResponse);
return Ok(urls); // Assume nothing went wrong if this line is reached.
}

pub struct RequestReponse {
Expand Down Expand Up @@ -299,6 +298,6 @@ fn test_parse_device1() {
<presentationURL>http://192.168.0.1/</presentationURL>
</device>
</root>"#;

assert_eq!(parse_control_url(text.as_bytes()).unwrap(), "/ctl/IPConn");
let urls = parse_control_url(text.as_bytes()).unwrap();
assert_eq!(urls.len() > 0, true);
}
55 changes: 50 additions & 5 deletions src/search.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,62 @@ pub fn search_gateway(options: SearchOptions) -> Result<Gateway, SearchError> {
let text = str::from_utf8(&buf[..read])?;

let location = parsing::parse_search_result(text)?;

if let Ok(control_url) = get_control_url(&location) {
return Ok(Gateway {
// Defaults to using the first control url.
if control_url.len() > 0{
return Ok(Gateway {
addr: location.0,
control_url: control_url,
});
control_url: control_url[0].clone(),
});
} else {
return Err(SearchError::InvalidResponse);
}
}
}
}

fn get_control_url(location: &(SocketAddrV4, String)) -> Result<String, SearchError> {
pub fn get_control_urls(options: SearchOptions) -> Result<Vec<String>, SearchError> {
let socket = UdpSocket::bind(options.bind_addr)?;
socket.set_read_timeout(options.timeout)?;

socket.send_to(messages::SEARCH_REQUEST.as_bytes(), options.broadcast_address)?;
loop {
let mut buf = [0u8; 1500];
let (read, _) = socket.recv_from(&mut buf)?;
let text = str::from_utf8(&buf[..read])?;

let location = parsing::parse_search_result(text)?;
if let Ok(control_url) = get_control_url(&location) {
if control_url.len() > 0{
return Ok(control_url);
} else {
return Err(SearchError::InvalidResponse);
}
}
}
}

/*
A bit of an ugly temporary workaround, basically the idea is to use get_control_urls() and then call
search_gateway() with a valid control url. Allows the user to select the interface to use.
*/
pub fn get_gateway_with_control_url(options: SearchOptions, url: &str) -> Result<Gateway, SearchError>{
let mut gateway = search_gateway(options)?;
gateway.control_url = String::from(url);
Ok(gateway)
}


fn get_control_url(location: &(SocketAddrV4, String)) -> Result<Vec<String>, SearchError> {
let url = format!("http://{}:{}{}", location.0.ip(), location.0.port(), location.1);
let response = attohttpc::get(&url).send()?;
parsing::parse_control_url(&response.bytes()?[..])
let res = parsing::parse_control_url(&response.bytes()?[..]);
res
}

// #[test]
// fn test_get_control_urls(){
// // This test will fail if upnp is disabled on the default interface ( default gateway )
// assert_eq!(get_control_urls(SearchOptions::default()).unwrap().len() > 0, true);
// }

0 comments on commit 72dec06

Please sign in to comment.