diff options
Diffstat (limited to 'Mono.Nat/Upnp/UpnpNatDevice.cs')
| -rw-r--r-- | Mono.Nat/Upnp/UpnpNatDevice.cs | 433 |
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> |
