async Task <ResponseMessage> DecodeMessageFromResponse(Stream s, int length) { StringBuilder data = new StringBuilder(); int bytesRead; byte[] buffer = BufferHelpers.Rent(); try { // Read out the content of the message, hopefully picking everything up in the case where we have no contentlength if (length != -1) { while (length > 0) { bytesRead = await s.ReadAsync(buffer, 0, Math.Min(buffer.Length, length)).ConfigureAwait(false); data.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)); length -= bytesRead; } } else { while ((bytesRead = await s.ReadAsync(buffer, 0, buffer.Length).ConfigureAwait(false)) != 0) { data.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead)); } } } finally { BufferHelpers.Release(buffer); } // Once we have our content, we need to see what kind of message it is. If we received // an error message we will immediately throw a MappingException. var dataString = data.ToString(); Log.InfoFormatted("uPnP Response: {0}, {1}", Environment.NewLine, dataString); return(ResponseMessage.Decode(this, dataString)); }
async Task <UpnpNatDevice> ServicesReceived(IPAddress localAddress, Uri deviceServiceUri, HttpWebResponse response, CancellationToken token) { Stream s = response.GetResponseStream(); if (response.StatusCode != HttpStatusCode.OK) { Log.ErrorFormatted("Couldn't get services list from: {0}. Return code was: {1}", response.ResponseUri, response.StatusCode); return(null); // FIXME: This the best thing to do?? } int abortCount = 0; StringBuilder servicesXml = new StringBuilder(); XmlDocument xmldoc = new XmlDocument(); byte[] buffer = BufferHelpers.Rent(); try { while (true) { var bytesRead = await s.ReadAsync(buffer, 0, buffer.Length, token).ConfigureAwait(false); 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 5 seconds, 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++ > 5000) { return(null); } Log.InfoFormatted("Couldn't parse services list from {0}", response.ResponseUri); await Task.Delay(10, token).ConfigureAwait(false); } } } finally { BufferHelpers.Release(buffer); } Log.InfoFormatted("Parsed services list {0}", response.ResponseUri); 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) { string serviceType = service["serviceType"].InnerText; Log.InfoFormatted("Found service {1} from service list {0}", response.ResponseUri, serviceType); // TODO: Add support for version 2 of UPnP. if (SupportedServices.Contains(serviceType, StringComparer.OrdinalIgnoreCase)) { var controlUrl = new Uri(service["controlURL"].InnerText, UriKind.RelativeOrAbsolute); IPEndPoint deviceEndpoint = new IPEndPoint(IPAddress.Parse(response.ResponseUri.Host), response.ResponseUri.Port); Log.InfoFormatted("Found upnp control uri at {1} from service url {0}", response.ResponseUri, controlUrl.OriginalString); try { if (controlUrl.IsAbsoluteUri) { deviceEndpoint = new IPEndPoint(IPAddress.Parse(controlUrl.Host), controlUrl.Port); Log.InfoFormatted("New control url {1} for device endpoint {0}", deviceEndpoint, controlUrl); } else { controlUrl = new Uri(deviceServiceUri, controlUrl.OriginalString); } } catch { controlUrl = new Uri(deviceServiceUri, controlUrl.OriginalString); Log.InfoFormatted("{0}: Assuming control Uri is relative: {1}", deviceEndpoint, controlUrl); } Log.InfoFormatted("Handshake Complete for {0}", deviceEndpoint); return(new UpnpNatDevice(localAddress, deviceEndpoint, controlUrl, serviceType)); } } } //If we get here, it means that we didn't get WANIPConnection/WANPPPConnection service, which means no uPnP forwarding //So we don't invoke the callback, so this device is never added to our lists return(null); }