aboutsummaryrefslogtreecommitdiff
path: root/Mono.Nat/Upnp/UpnpNatDevice.cs
diff options
context:
space:
mode:
Diffstat (limited to 'Mono.Nat/Upnp/UpnpNatDevice.cs')
-rw-r--r--Mono.Nat/Upnp/UpnpNatDevice.cs433
1 files changed, 3 insertions, 430 deletions
diff --git a/Mono.Nat/Upnp/UpnpNatDevice.cs b/Mono.Nat/Upnp/UpnpNatDevice.cs
index 6c4bcd7467..22f8ff968d 100644
--- a/Mono.Nat/Upnp/UpnpNatDevice.cs
+++ b/Mono.Nat/Upnp/UpnpNatDevice.cs
@@ -70,7 +70,7 @@ namespace Mono.Nat.Upnp
// FIXME: Is this reliable enough. What if we get a hostname as opposed to a proper http address
// Are we going to get addresses with the "http://" attached?
- if (locationDetails.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
+ if (locationDetails.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
{
NatUtility.Log("Found device at: {0}", locationDetails);
// This bit strings out the "http://" from the string
@@ -98,7 +98,7 @@ namespace Mono.Nat.Upnp
this.localAddress = localAddress;
// Split the string at the "location" section so i can extract the ipaddress and service description url
- string locationDetails = deviceDetails.Substring(deviceDetails.IndexOf("Location", StringComparison.InvariantCultureIgnoreCase) + 9).Split('\r')[0];
+ string locationDetails = deviceDetails.Substring(deviceDetails.IndexOf("Location", StringComparison.OrdinalIgnoreCase) + 9).Split('\r')[0];
this.serviceType = serviceType;
// Make sure we have no excess whitespace
@@ -106,7 +106,7 @@ namespace Mono.Nat.Upnp
// FIXME: Is this reliable enough. What if we get a hostname as opposed to a proper http address
// Are we going to get addresses with the "http://" attached?
- if (locationDetails.StartsWith("http://", StringComparison.InvariantCultureIgnoreCase))
+ if (locationDetails.StartsWith("http://", StringComparison.OrdinalIgnoreCase))
{
NatUtility.Log("Found device at: {0}", locationDetails);
// This bit strings out the "http://" from the string
@@ -171,189 +171,12 @@ namespace Mono.Nat.Upnp
get { return serviceType; }
}
- /// <summary>
- /// Begins an async call to get the external ip address of the router
- /// </summary>
- public override IAsyncResult BeginGetExternalIP(AsyncCallback callback, object asyncState)
- {
- // Create the port map message
- GetExternalIPAddressMessage message = new GetExternalIPAddressMessage(this);
- return BeginMessageInternal(message, callback, asyncState, EndGetExternalIPInternal);
- }
-
- /// <summary>
- /// Maps the specified port to this computer
- /// </summary>
- public override IAsyncResult BeginCreatePortMap(Mapping mapping, AsyncCallback callback, object asyncState)
- {
- CreatePortMappingMessage message = new CreatePortMappingMessage(mapping, localAddress, this);
- return BeginMessageInternal(message, callback, asyncState, EndCreatePortMapInternal);
- }
-
public override Task CreatePortMap(Mapping mapping)
{
CreatePortMappingMessage message = new CreatePortMappingMessage(mapping, localAddress, this);
return _httpClient.SendAsync(message.Encode(), message.Method);
}
- /// <summary>
- /// Removes a port mapping from this computer
- /// </summary>
- public override IAsyncResult BeginDeletePortMap(Mapping mapping, AsyncCallback callback, object asyncState)
- {
- DeletePortMappingMessage message = new DeletePortMappingMessage(mapping, this);
- return BeginMessageInternal(message, callback, asyncState, EndDeletePortMapInternal);
- }
-
-
- public override IAsyncResult BeginGetAllMappings(AsyncCallback callback, object asyncState)
- {
- GetGenericPortMappingEntry message = new GetGenericPortMappingEntry(0, this);
- return BeginMessageInternal(message, callback, asyncState, EndGetAllMappingsInternal);
- }
-
-
- public override IAsyncResult BeginGetSpecificMapping(Protocol protocol, int port, AsyncCallback callback, object asyncState)
- {
- GetSpecificPortMappingEntryMessage message = new GetSpecificPortMappingEntryMessage(protocol, port, this);
- return this.BeginMessageInternal(message, callback, asyncState, new AsyncCallback(this.EndGetSpecificMappingInternal));
- }
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="result"></param>
- public override void EndCreatePortMap(IAsyncResult result)
- {
- if (result == null) throw new ArgumentNullException("result");
-
- PortMapAsyncResult mappingResult = result as PortMapAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- // Check if we need to wait for the operation to finish
- if (!result.IsCompleted)
- result.AsyncWaitHandle.WaitOne();
-
- // If we have a saved exception, it means something went wrong during the mapping
- // so we just rethrow the exception and let the user figure out what they should do.
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
- throw new MappingException(msg.ErrorCode, msg.Description);
- }
-
- //return result.AsyncState as Mapping;
- }
-
-
- /// <summary>
- ///
- /// </summary>
- /// <param name="result"></param>
- public override void EndDeletePortMap(IAsyncResult result)
- {
- if (result == null)
- throw new ArgumentNullException("result");
-
- PortMapAsyncResult mappingResult = result as PortMapAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- // Check if we need to wait for the operation to finish
- if (!mappingResult.IsCompleted)
- mappingResult.AsyncWaitHandle.WaitOne();
-
- // If we have a saved exception, it means something went wrong during the mapping
- // so we just rethrow the exception and let the user figure out what they should do.
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
- throw new MappingException(msg.ErrorCode, msg.Description);
- }
-
- // If all goes well, we just return
- //return true;
- }
-
-
- public override Mapping[] EndGetAllMappings(IAsyncResult result)
- {
- if (result == null)
- throw new ArgumentNullException("result");
-
- GetAllMappingsAsyncResult mappingResult = result as GetAllMappingsAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- if (!mappingResult.IsCompleted)
- mappingResult.AsyncWaitHandle.WaitOne();
-
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
- if (msg.ErrorCode != 713)
- throw new MappingException(msg.ErrorCode, msg.Description);
- }
-
- return mappingResult.Mappings.ToArray();
- }
-
-
- /// <summary>
- /// Ends an async request to get the external ip address of the router
- /// </summary>
- public override IPAddress EndGetExternalIP(IAsyncResult result)
- {
- if (result == null) throw new ArgumentNullException("result");
-
- PortMapAsyncResult mappingResult = result as PortMapAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- if (!result.IsCompleted)
- result.AsyncWaitHandle.WaitOne();
-
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage msg = mappingResult.SavedMessage as ErrorMessage;
- throw new MappingException(msg.ErrorCode, msg.Description);
- }
-
- if (mappingResult.SavedMessage == null)
- return null;
- else
- return ((GetExternalIPAddressResponseMessage)mappingResult.SavedMessage).ExternalIPAddress;
- }
-
-
- public override Mapping EndGetSpecificMapping(IAsyncResult result)
- {
- if (result == null)
- throw new ArgumentNullException("result");
-
- GetAllMappingsAsyncResult mappingResult = result as GetAllMappingsAsyncResult;
- if (mappingResult == null)
- throw new ArgumentException("Invalid AsyncResult", "result");
-
- if (!mappingResult.IsCompleted)
- mappingResult.AsyncWaitHandle.WaitOne();
-
- if (mappingResult.SavedMessage is ErrorMessage)
- {
- ErrorMessage message = mappingResult.SavedMessage as ErrorMessage;
- if (message.ErrorCode != 0x2ca)
- {
- throw new MappingException(message.ErrorCode, message.Description);
- }
- }
- if (mappingResult.Mappings.Count == 0)
- return new Mapping(Protocol.Tcp, -1, -1);
-
- return mappingResult.Mappings[0];
- }
-
-
public override bool Equals(object obj)
{
UpnpNatDevice device = obj as UpnpNatDevice;
@@ -373,256 +196,6 @@ namespace Mono.Nat.Upnp
return (this.hostEndPoint.GetHashCode() ^ this.controlUrl.GetHashCode() ^ this.serviceDescriptionUrl.GetHashCode());
}
- private IAsyncResult BeginMessageInternal(MessageBase message, AsyncCallback storedCallback, object asyncState, AsyncCallback callback)
- {
- byte[] body;
- WebRequest request = message.Encode(out body);
- PortMapAsyncResult mappingResult = PortMapAsyncResult.Create(message, request, storedCallback, asyncState);
-
- if (body.Length > 0)
- {
- request.ContentLength = body.Length;
- request.BeginGetRequestStream(delegate (IAsyncResult result)
- {
- try
- {
- Stream s = request.EndGetRequestStream(result);
- s.Write(body, 0, body.Length);
- request.BeginGetResponse(callback, mappingResult);
- }
- catch (Exception ex)
- {
- mappingResult.Complete(ex);
- }
- }, null);
- }
- else
- {
- request.BeginGetResponse(callback, mappingResult);
- }
- return mappingResult;
- }
-
- private void CompleteMessage(IAsyncResult result)
- {
- PortMapAsyncResult mappingResult = result.AsyncState as PortMapAsyncResult;
- mappingResult.CompletedSynchronously = result.CompletedSynchronously;
- mappingResult.Complete();
- }
-
- private MessageBase DecodeMessageFromResponse(Stream s, long length)
- {
- StringBuilder data = new StringBuilder();
- int bytesRead = 0;
- int totalBytesRead = 0;
- byte[] buffer = new byte[10240];
-
- // Read out the content of the message, hopefully picking everything up in the case where we have no contentlength
- if (length != -1)
- {
- while (totalBytesRead < length)
- {
- bytesRead = s.Read(buffer, 0, buffer.Length);
- data.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
- totalBytesRead += bytesRead;
- }
- }
- else
- {
- while ((bytesRead = s.Read(buffer, 0, buffer.Length)) != 0)
- data.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
- }
-
- // Once we have our content, we need to see what kind of message it is. It'll either a an error
- // or a response based on the action we performed.
- return MessageBase.Decode(this, data.ToString());
- }
-
- private void EndCreatePortMapInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
- CompleteMessage(result);
- }
-
- private void EndMessageInternal(IAsyncResult result)
- {
- HttpWebResponse response = null;
- PortMapAsyncResult mappingResult = result.AsyncState as PortMapAsyncResult;
-
- try
- {
- try
- {
- response = (HttpWebResponse)mappingResult.Request.EndGetResponse(result);
- }
- catch (WebException ex)
- {
- // Even if the request "failed" i want to continue on to read out the response from the router
- response = ex.Response as HttpWebResponse;
- if (response == null)
- mappingResult.SavedMessage = new ErrorMessage((int)ex.Status, ex.Message);
- }
- if (response != null)
- mappingResult.SavedMessage = DecodeMessageFromResponse(response.GetResponseStream(), response.ContentLength);
- }
-
- finally
- {
- if (response != null)
- response.Close();
- }
- }
-
- private void EndDeletePortMapInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
- CompleteMessage(result);
- }
-
- private void EndGetAllMappingsInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
-
- GetAllMappingsAsyncResult mappingResult = result.AsyncState as GetAllMappingsAsyncResult;
- GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
- if (message != null)
- {
- Mapping mapping = new Mapping(message.Protocol, message.InternalPort, message.ExternalPort, message.LeaseDuration);
- mapping.Description = message.PortMappingDescription;
- mappingResult.Mappings.Add(mapping);
- GetGenericPortMappingEntry next = new GetGenericPortMappingEntry(mappingResult.Mappings.Count, this);
-
- // It's ok to do this synchronously because we should already be on anther thread
- // and this won't block the user.
- byte[] body;
- WebRequest request = next.Encode(out body);
- if (body.Length > 0)
- {
- request.ContentLength = body.Length;
- request.GetRequestStream().Write(body, 0, body.Length);
- }
- mappingResult.Request = request;
- request.BeginGetResponse(EndGetAllMappingsInternal, mappingResult);
- return;
- }
-
- CompleteMessage(result);
- }
-
- private void EndGetExternalIPInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
- CompleteMessage(result);
- }
-
- private void EndGetSpecificMappingInternal(IAsyncResult result)
- {
- EndMessageInternal(result);
-
- GetAllMappingsAsyncResult mappingResult = result.AsyncState as GetAllMappingsAsyncResult;
- GetGenericPortMappingEntryResponseMessage message = mappingResult.SavedMessage as GetGenericPortMappingEntryResponseMessage;
- if (message != null)
- {
- Mapping mapping = new Mapping(mappingResult.SpecificMapping.Protocol, message.InternalPort, mappingResult.SpecificMapping.PublicPort, message.LeaseDuration);
- mapping.Description = mappingResult.SpecificMapping.Description;
- mappingResult.Mappings.Add(mapping);
- }
-
- CompleteMessage(result);
- }
-
- internal async Task<bool> GetServicesList()
- {
- // Create a HTTPWebRequest to download the list of services the device offers
- var requestOptions = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint, _logger).Encode();
-
- requestOptions.BufferContent = false;
-
- using (var response = await _httpClient.Get(requestOptions).ConfigureAwait(false))
- {
- return ServicesReceived(response);
- }
- }
-
- private bool ServicesReceived(Stream s)
- {
- int abortCount = 0;
- int bytesRead = 0;
- byte[] buffer = new byte[10240];
- StringBuilder servicesXml = new StringBuilder();
- XmlDocument xmldoc = new XmlDocument();
-
- while (true)
- {
- bytesRead = s.Read(buffer, 0, buffer.Length);
- servicesXml.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
- try
- {
- xmldoc.LoadXml(servicesXml.ToString());
- break;
- }
- catch (XmlException)
- {
- // If we can't receive the entire XML within 500ms, then drop the connection
- // Unfortunately not all routers supply a valid ContentLength (mine doesn't)
- // so this hack is needed to keep testing our recieved data until it gets successfully
- // parsed by the xmldoc. Without this, the code will never pick up my router.
- if (abortCount++ > 50)
- {
- return false;
- }
- NatUtility.Log("{0}: Couldn't parse services list", HostEndPoint);
- System.Threading.Thread.Sleep(10);
- }
- }
-
- NatUtility.Log("{0}: Parsed services list", HostEndPoint);
- XmlNamespaceManager ns = new XmlNamespaceManager(xmldoc.NameTable);
- ns.AddNamespace("ns", "urn:schemas-upnp-org:device-1-0");
- XmlNodeList nodes = xmldoc.SelectNodes("//*/ns:serviceList", ns);
-
- foreach (XmlNode node in nodes)
- {
- //Go through each service there
- foreach (XmlNode service in node.ChildNodes)
- {
- //If the service is a WANIPConnection, then we have what we want
- string type = service["serviceType"].InnerText;
- NatUtility.Log("{0}: Found service: {1}", HostEndPoint, type);
- StringComparison c = StringComparison.OrdinalIgnoreCase;
- // TODO: Add support for version 2 of UPnP.
- if (type.Equals("urn:schemas-upnp-org:service:WANPPPConnection:1", c) ||
- type.Equals("urn:schemas-upnp-org:service:WANIPConnection:1", c))
- {
- this.controlUrl = service["controlURL"].InnerText;
- NatUtility.Log("{0}: Found upnp service at: {1}", HostEndPoint, controlUrl);
- try
- {
- Uri u = new Uri(controlUrl);
- if (u.IsAbsoluteUri)
- {
- EndPoint old = hostEndPoint;
- this.hostEndPoint = new IPEndPoint(IPAddress.Parse(u.Host), u.Port);
- NatUtility.Log("{0}: Absolute URI detected. Host address is now: {1}", old, HostEndPoint);
- this.controlUrl = controlUrl.Substring(u.GetLeftPart(UriPartial.Authority).Length);
- NatUtility.Log("{0}: New control url: {1}", HostEndPoint, controlUrl);
- }
- }
- catch
- {
- NatUtility.Log("{0}: Assuming control Uri is relative: {1}", HostEndPoint, controlUrl);
- }
- NatUtility.Log("{0}: Handshake Complete", HostEndPoint);
- return true;
- }
- }
- }
-
- //If we get here, it means that we didn't get WANIPConnection service, which means no uPnP forwarding
- //So we don't invoke the callback, so this device is never added to our lists
- return false;
- }
-
/// <summary>
/// Overridden.
/// </summary>