Exemple #1
0
        public void Handle(IPAddress localAddres, byte[] response)
        {
            //if (!IsSearchAddress(endpoint.Address))
            //    return;
            if (response.Length != 12)
            {
                return;
            }
            if (response[0] != PmpConstants.Version)
            {
                return;
            }
            if (response[1] != PmpConstants.ServerNoop)
            {
                return;
            }
            int errorcode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(response, 2));

            if (errorcode != 0)
            {
                NatUtility.Log("Non zero error: {0}", errorcode);
            }

            IPAddress publicIp = new IPAddress(new byte[] { response[8], response[9], response[10], response[11] });

            OnDeviceFound(new DeviceEventArgs(new PmpNatDevice(localAddres, publicIp)));
        }
Exemple #2
0
        protected HttpRequestOptions CreateRequest(string upnpMethod, string methodParameters)
        {
            string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;

            NatUtility.Log("Initiating request to: {0}", ss);

            var req = new HttpRequestOptions();

            req.LogErrors = false;

            // The periodic request logging may keep some devices awake
            req.LogRequestAsDebug = true;

            req.Url                     = ss;
            req.EnableKeepAlive         = false;
            req.RequestContentType      = "text/xml";
            req.AppendCharsetToMimeType = true;
            req.RequestHeaders.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\"");

            string bodyString = "<s:Envelope "
                                + "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
                                + "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
                                + "<s:Body>"
                                + "<u:" + upnpMethod + " "
                                + "xmlns:u=\"" + device.ServiceType + "\">"
                                + methodParameters
                                + "</u:" + upnpMethod + ">"
                                + "</s:Body>"
                                + "</s:Envelope>\r\n\r\n";

            req.RequestContentBytes = System.Text.Encoding.UTF8.GetBytes(bodyString);
            return(req);
        }
Exemple #3
0
        protected override Task HandleMessageReceived(IPAddress localAddress, UdpReceiveResult result, CancellationToken token)
        {
            var response = result.Buffer;
            var endpoint = result.RemoteEndPoint;

            if (response.Length != 12)
            {
                return(Task.CompletedTask);
            }
            if (response [0] != PmpConstants.Version)
            {
                return(Task.CompletedTask);
            }
            if (response [1] != PmpConstants.ServerNoop)
            {
                return(Task.CompletedTask);
            }

            int errorcode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(response, 2));

            if (errorcode != 0)
            {
                NatUtility.Log("Non zero error: {0}", errorcode);
            }

            var publicIp = new IPAddress(new byte [] { response [8], response [9], response [10], response [11] });

            RaiseDeviceFound(new PmpNatDevice(endpoint, publicIp));
            return(Task.CompletedTask);
        }
Exemple #4
0
        protected WebRequest CreateRequest(string upnpMethod, string methodParameters, out byte[] body)
        {
            string ss = "http://" + this.device.HostEndPoint.ToString() + this.device.ControlUrl;

            NatUtility.Log("Initiating request to: {0}", ss);
            Uri location = new Uri(ss);

            HttpWebRequest req = (HttpWebRequest)HttpWebRequest.Create(location);

            req.KeepAlive   = false;
            req.Method      = "POST";
            req.ContentType = "text/xml; charset=\"utf-8\"";
            req.Headers.Add("SOAPACTION", "\"" + device.ServiceType + "#" + upnpMethod + "\"");

            string bodyString = "<s:Envelope "
                                + "xmlns:s=\"http://schemas.xmlsoap.org/soap/envelope/\" "
                                + "s:encodingStyle=\"http://schemas.xmlsoap.org/soap/encoding/\">"
                                + "<s:Body>"
                                + "<u:" + upnpMethod + " "
                                + "xmlns:u=\"" + device.ServiceType + "\">"
                                + methodParameters
                                + "</u:" + upnpMethod + ">"
                                + "</s:Body>"
                                + "</s:Envelope>\r\n\r\n";

            body = System.Text.Encoding.UTF8.GetBytes(bodyString);
            return(req);
        }
        internal UpnpNatDevice(IPAddress localAddress, UpnpDeviceInfo deviceInfo, IPEndPoint hostEndPoint, string serviceType)
        {
            this.LastSeen     = DateTime.Now;
            this.localAddress = localAddress;

            // Split the string at the "location" section so i can extract the ipaddress and service description url
            string locationDetails = deviceInfo.Location.ToString();

            this.serviceType = serviceType;

            // Make sure we have no excess whitespace
            locationDetails = locationDetails.Trim();

            // 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))
            {
                NatUtility.Log("Found device at: {0}", locationDetails);
                // This bit strings out the "http://" from the string
                locationDetails = locationDetails.Substring(7);

                this.hostEndPoint = hostEndPoint;

                NatUtility.Log("Parsed device as: {0}", this.hostEndPoint.ToString());

                // The service description URL is the remainder of the "locationDetails" string. The bit that was originally after the ip
                // and port information
                this.serviceDescriptionUrl = locationDetails.Substring(locationDetails.IndexOf('/'));
            }
            else
            {
                NatUtility.Log("Couldn't decode address. Please send following string to the developer: ");
            }
        }
Exemple #6
0
        private async Task <Mapping> InternalCreatePortMapAsync(Mapping mapping, bool create)
        {
            var package = new List <byte>();

            package.Add(PmpConstants.Version);
            package.Add(mapping.Protocol == Protocol.Tcp ? PmpConstants.OperationCodeTcp : PmpConstants.OperationCodeUdp);
            package.Add(0); //reserved
            package.Add(0); //reserved
            package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short)mapping.PrivatePort)));
            package.AddRange(
                BitConverter.GetBytes(create ? IPAddress.HostToNetworkOrder((short)mapping.PublicPort) : (short)0));
            package.AddRange(BitConverter.GetBytes(IPAddress.HostToNetworkOrder(mapping.Lifetime)));

            try
            {
                byte[] buffer  = package.ToArray(package.Count);
                int    attempt = 0;
                int    delay   = PmpConstants.RetryDelay;

                using (var udpClient = new UdpClient())
                {
                    var cancellationTokenSource = new CancellationTokenSource();

                    while (attempt < PmpConstants.RetryAttempts)
                    {
                        await udpClient.SendAsync(buffer, buffer.Length,
                                                  new IPEndPoint(LocalAddress, PmpConstants.ServerPort));

                        if (attempt == 0)
                        {
                            Task.Run(() => CreatePortMapListen(udpClient, mapping, cancellationTokenSource.Token));
                        }

                        attempt++;
                        delay *= 2;
                        await Task.Delay(delay).ConfigureAwait(false);
                    }

                    cancellationTokenSource.Cancel();
                }
            }
            catch (OperationCanceledException)
            {
            }
            catch (Exception e)
            {
                string type    = create ? "create" : "delete";
                string message = String.Format("Failed to {0} portmap (protocol={1}, private port={2}) {3}",
                                               type,
                                               mapping.Protocol,
                                               mapping.PrivatePort,
                                               e.Message);
                NatUtility.Log(message);
                var pmpException = e as MappingException;
                throw new MappingException(message, pmpException);
            }

            return(mapping);
        }
Exemple #7
0
        public static MessageBase Decode(UpnpNatDevice device, string message)
        {
            XmlNode     node;
            XmlDocument doc = new XmlDocument();

            doc.LoadXml(message);

            XmlNamespaceManager nsm = new XmlNamespaceManager(doc.NameTable);

            // Error messages should be found under this namespace
            nsm.AddNamespace("errorNs", "urn:schemas-upnp-org:control-1-0");
            nsm.AddNamespace("responseNs", device.ServiceType);

            // Check to see if we have a fault code message.
            if ((node = doc.SelectSingleNode("//errorNs:UPnPError", nsm)) != null)
            {
                string errorCode        = node["errorCode"] != null ? node["errorCode"].InnerText : "";
                string errorDescription = node["errorDescription"] != null ? node["errorDescription"].InnerText : "";

                return(new ErrorMessage(Convert.ToInt32(errorCode, CultureInfo.InvariantCulture), errorDescription));
            }

            if ((doc.SelectSingleNode("//responseNs:AddPortMappingResponse", nsm)) != null)
            {
                return(new CreatePortMappingResponseMessage());
            }

            if ((doc.SelectSingleNode("//responseNs:DeletePortMappingResponse", nsm)) != null)
            {
                return(new DeletePortMapResponseMessage());
            }

            if ((node = doc.SelectSingleNode("//responseNs:GetExternalIPAddressResponse", nsm)) != null)
            {
                string newExternalIPAddress = node["NewExternalIPAddress"] != null ? node["NewExternalIPAddress"].InnerText : "";
                return(new GetExternalIPAddressResponseMessage(newExternalIPAddress));
            }

            if ((node = doc.SelectSingleNode("//responseNs:GetGenericPortMappingEntryResponse", nsm)) != null)
            {
                return(new GetGenericPortMappingEntryResponseMessage(node, true));
            }

            if ((node = doc.SelectSingleNode("//responseNs:GetSpecificPortMappingEntryResponse", nsm)) != null)
            {
                return(new GetGenericPortMappingEntryResponseMessage(node, false));
            }

            NatUtility.Log("Unknown message returned. Please send me back the following XML:");
            NatUtility.Log(message);
            return(null);
        }
Exemple #8
0
        async Task <UpnpNatDevice> GetServicesList(IPAddress localAddress, Uri deviceServiceUri, CancellationToken token)
        {
            // Create a HTTPWebRequest to download the list of services the device offers
            var request = new GetServicesMessage(deviceServiceUri).Encode(out byte[] body);

            if (body.Length > 0)
            {
                NatUtility.Log("Error: Services Message contained a body");
            }
            using (token.Register(() => request.Abort()))
                using (var response = (HttpWebResponse)await request.GetResponseAsync().ConfigureAwait(false))
                    return(await ServicesReceived(localAddress, deviceServiceUri, response).ConfigureAwait(false));
        }
Exemple #9
0
        public virtual Task <UpnpNatDevice> Handle(IPAddress localAddress, byte[] response, IPEndPoint endpoint)
        {
            // Convert it to a string for easy parsing
            string dataString = null;


            string urn;

            dataString = Encoding.UTF8.GetString(response);

            if (NatUtility.Verbose)
            {
                NatUtility.Log("UPnP Response: {0}", dataString);
            }

            /* For UPnP Port Mapping we need ot find either WANPPPConnection or WANIPConnection.
             *  Any other device type is no good to us for this purpose. See the IGP overview paper
             *  page 5 for an overview of device types and their hierarchy.
             *  http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf */

            /* TODO: Currently we are assuming version 1 of the protocol. We should figure out which
             *  version it is and apply the correct URN. */

            /* Some routers don't correctly implement the version ID on the URN, so we only search for the type
             *  prefix. */

            string           log = "UPnP Response: Router advertised a '{0}' service";
            StringComparison c   = StringComparison.OrdinalIgnoreCase;

            if (dataString.IndexOf("urn:schemas-upnp-org:service:WANIPConnection:", c) != -1)
            {
                urn = "urn:schemas-upnp-org:service:WANIPConnection:1";
                NatUtility.Log(log, "urn:schemas-upnp-org:service:WANIPConnection:1");
            }
            else if (dataString.IndexOf("urn:schemas-upnp-org:service:WANPPPConnection:", c) != -1)
            {
                urn = "urn:schemas-upnp-org:service:WANPPPConnection:1";
                NatUtility.Log(log, "urn:schemas-upnp-org:service:WANPPPConnection:");
            }
            else
            {
                throw new NotSupportedException("Received non-supported device type");
            }

            // We have an internet gateway device now
            var device = new UpnpNatDevice(localAddress, dataString, urn, Logger, HttpClient);

            return(Task.FromResult(device));
        }
Exemple #10
0
        internal UpnpNatDevice(IPAddress localAddress, string deviceDetails, string serviceType, ILogger logger, IHttpClient httpClient)
        {
            _logger           = logger;
            _httpClient       = httpClient;
            this.LastSeen     = DateTime.Now;
            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.OrdinalIgnoreCase) + 9).Split('\r')[0];

            this.serviceType = serviceType;

            // Make sure we have no excess whitespace
            locationDetails = locationDetails.Trim();

            // 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.OrdinalIgnoreCase))
            {
                NatUtility.Log("Found device at: {0}", locationDetails);
                // This bit strings out the "http://" from the string
                locationDetails = locationDetails.Substring(7);

                // We then split off the end of the string to get something like: 192.168.0.3:241 in our string
                string hostAddressAndPort = locationDetails.Remove(locationDetails.IndexOf('/'));

                // From this we parse out the IP address and Port
                if (hostAddressAndPort.IndexOf(':') > 0)
                {
                    this.hostEndPoint = new IPEndPoint(IPAddress.Parse(hostAddressAndPort.Remove(hostAddressAndPort.IndexOf(':'))),
                                                       Convert.ToUInt16(hostAddressAndPort.Substring(hostAddressAndPort.IndexOf(':') + 1), System.Globalization.CultureInfo.InvariantCulture));
                }
                else
                {
                    // there is no port specified, use default port (80)
                    this.hostEndPoint = new IPEndPoint(IPAddress.Parse(hostAddressAndPort), 80);
                }

                NatUtility.Log("Parsed device as: {0}", this.hostEndPoint.ToString());

                // The service description URL is the remainder of the "locationDetails" string. The bit that was originally after the ip
                // and port information
                this.serviceDescriptionUrl = locationDetails.Substring(locationDetails.IndexOf('/'));
            }
            else
            {
                logger.Warn("Couldn't decode address: " + deviceDetails);
            }
        }
        internal void GetServicesList(NatDeviceCallback callback)
        {
            // Save the callback so i can use it again later when i've finished parsing the services available
            this.callback = callback;

            // Create a HTTPWebRequest to download the list of services the device offers
            byte[]     body;
            WebRequest request = new GetServicesMessage(this.serviceDescriptionUrl, this.hostEndPoint).Encode(out body);

            if (body.Length > 0)
            {
                NatUtility.Log("Error: Services Message contained a body");
            }
            request.BeginGetResponse(this.ServicesReceived, request);
        }
Exemple #12
0
        async Task <ResponseMessage> SendMessageAsync(RequestMessage message)
        {
            HttpWebRequest request = message.Encode(out byte [] body);

            // If this device has multiple active network devices, ensure the web request is sent from the network device which
            // received the response from the router. That way when we attempt to map a port, the IPAddress we are mapping to
            // is the same as the IPAddress which issues the WebRequest. Most uPnP implementations don't allow a device to
            // forward a port to a *different* IP address.
            request.ServicePoint.BindIPEndPointDelegate = delegate(ServicePoint servicePoint, IPEndPoint remoteEndPoint, int retryCount) {
                NatUtility.Log($"The WebRequest being sent to {remoteEndPoint} has been bound to local IP address {LocalAddress}");
                return(new IPEndPoint(LocalAddress, 0));
            };

            if (NatUtility.Logger != null)
            {
                NatUtility.Log($"uPnP Request: {Environment.NewLine}{Encoding.UTF8.GetString (body)}");
            }

            if (body.Length > 0)
            {
                request.ContentLength = body.Length;
                using (var stream = await request.GetRequestStreamAsync().ConfigureAwait(false))
                    await stream.WriteAsync(body, 0, body.Length).ConfigureAwait(false);
            }

            try {
                using (var response = await request.GetResponseAsync().ConfigureAwait(false))
                    return(await DecodeMessageFromResponse(response.GetResponseStream(), (int)response.ContentLength));
            } catch (WebException ex) {
                // Even if the request "failed" i want to continue on to read out the response from the router
                using (var response = ex.Response as HttpWebResponse) {
                    if (response == null)
                    {
                        throw new MappingException("Unexpected error sending a message to the device", ex);
                    }
                    else
                    {
                        return(await DecodeMessageFromResponse(response.GetResponseStream(), (int)response.ContentLength));
                    }
                }
            }
        }
Exemple #13
0
        async Task <ResponseMessage> DecodeMessageFromResponse(Stream s, int length)
        {
            StringBuilder data = new StringBuilder();
            int           bytesRead;

            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 (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));
                }
            }

            // 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();

            if (NatUtility.Logger != null)
            {
                NatUtility.Log($"uPnP Response: {Environment.NewLine}{dataString}");
            }
            return(ResponseMessage.Decode(this, dataString));
        }
        private void ServicesReceived(IAsyncResult result)
        {
            HttpWebResponse response = null;

            try
            {
                int            abortCount  = 0;
                int            bytesRead   = 0;
                byte[]         buffer      = new byte[10240];
                StringBuilder  servicesXml = new StringBuilder();
                XmlDocument    xmldoc      = new XmlDocument();
                HttpWebRequest request     = result.AsyncState as HttpWebRequest;
                response = request.EndGetResponse(result) as HttpWebResponse;
                Stream s = response.GetResponseStream();

                if (response.StatusCode != HttpStatusCode.OK)
                {
                    NatUtility.Log("{0}: Couldn't get services list: {1}", HostEndPoint, response.StatusCode);
                    return;                     // FIXME: This the best thing to do??
                }

                while (true)
                {
                    bytesRead = s.Read(buffer, 0, buffer.Length);
                    servicesXml.Append(Encoding.UTF8.GetString(buffer, 0, bytesRead));
                    try
                    {
                        xmldoc.LoadXml(servicesXml.ToString());
                        response.Close();
                        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)
                        {
                            response.Close();
                            return;
                        }
                        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;
                        if (type.Equals(this.serviceType, 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);
                            this.callback(this);
                            return;
                        }
                    }
                }

                //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
            }
            catch (WebException ex)
            {
                // Just drop the connection, FIXME: Should i retry?
                NatUtility.Log("{0}: Device denied the connection attempt: {1}", HostEndPoint, ex);
            }
            finally
            {
                if (response != null)
                {
                    response.Close();
                }
            }
        }
Exemple #15
0
        protected override async Task HandleMessageReceived(IPAddress localAddress, UdpReceiveResult result, CancellationToken token)
        {
            // Convert it to a string for easy parsing
            string dataString = null;
            var    response   = result.Buffer;

            // No matter what, this method should never throw an exception. If something goes wrong
            // we should still be in a position to handle the next reply correctly.
            try {
                dataString = Encoding.UTF8.GetString(response);

                NatUtility.Log("uPnP Search Response: {0}", dataString);

                /* For UPnP Port Mapping we need ot find either WANPPPConnection or WANIPConnection.
                 * Any other device type is no good to us for this purpose. See the IGP overview paper
                 * page 5 for an overview of device types and their hierarchy.
                 * http://upnp.org/specs/gw/UPnP-gw-InternetGatewayDevice-v1-Device.pdf */

                /* TODO: Currently we are assuming version 1 of the protocol. We should figure out which
                 * version it is and apply the correct URN. */

                /* Some routers don't correctly implement the version ID on the URN, so we only search for the type
                 * prefix. */

                string           log = "uPnP Search Response: Router advertised a '{0}' service";
                StringComparison c   = StringComparison.OrdinalIgnoreCase;
                if (dataString.IndexOf("urn:schemas-upnp-org:service:WANIPConnection:", c) != -1)
                {
                    NatUtility.Log(log, "urn:schemas-upnp-org:service:WANIPConnection:1");
                }
                else if (dataString.IndexOf("urn:schemas-upnp-org:service:WANPPPConnection:", c) != -1)
                {
                    NatUtility.Log(log, "urn:schemas-upnp-org:service:WANPPPConnection:");
                }
                else
                {
                    return;
                }

                var location = dataString.Split(new [] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries)
                               .Select(t => t.Trim())
                               .FirstOrDefault(t => t.StartsWith("LOCATION", StringComparison.OrdinalIgnoreCase));

                if (location == null)
                {
                    return;
                }

                var deviceLocation   = location.Split(new [] { ':' }, 2).Skip(1).FirstOrDefault();
                var deviceServiceUri = new Uri(deviceLocation);

                using (await Locker.DisposableWaitAsync(token)) {
                    // If we send 3 requests at a time, ensure we only fetch the services list once
                    // even if three responses are received
                    if (LastFetched.TryGetValue(deviceServiceUri, out DateTime last))
                    {
                        if ((DateTime.Now - last) < TimeSpan.FromSeconds(20))
                        {
                            return;
                        }
                    }

                    LastFetched [deviceServiceUri] = DateTime.Now;
                }

                // Once we've parsed the information we need, we tell the device to retrieve it's service list
                // Once we successfully receive the service list, the callback provided will be invoked.
                NatUtility.Log("Fetching service list: {0}", deviceServiceUri);
                var d = await GetServicesList(localAddress, deviceServiceUri, token).ConfigureAwait(false);

                if (d != null)
                {
                    RaiseDeviceFound(d);
                }
            } catch (Exception ex) {
                Trace.WriteLine("Unhandled exception when trying to decode a device's response Send me the following data: ");
                Trace.WriteLine("ErrorMessage:");
                Trace.WriteLine(ex.Message);
                Trace.WriteLine("Data string:");
                Trace.WriteLine(dataString);
            }
        }
Exemple #16
0
        private async void CreatePortMapListen(UdpClient udpClient, Mapping mapping, CancellationToken cancellationToken)
        {
            while (!cancellationToken.IsCancellationRequested)
            {
                try
                {
                    var result = await udpClient.ReceiveAsync().ConfigureAwait(false);

                    var    endPoint = result.RemoteEndPoint;
                    byte[] data     = data = result.Buffer;

                    if (data.Length < 16)
                    {
                        continue;
                    }

                    if (data[0] != PmpConstants.Version)
                    {
                        continue;
                    }

                    var opCode = (byte)(data[1] & 127);

                    var protocol = Protocol.Tcp;
                    if (opCode == PmpConstants.OperationCodeUdp)
                    {
                        protocol = Protocol.Udp;
                    }

                    short resultCode = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 2));
                    int   epoch      = IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 4));

                    short privatePort = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 8));
                    short publicPort  = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(data, 10));

                    var lifetime = (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(data, 12));

                    if (privatePort < 0 || publicPort < 0 || resultCode != PmpConstants.ResultCodeSuccess)
                    {
                        var errors = new[]
                        {
                            "Success",
                            "Unsupported Version",
                            "Not Authorized/Refused (e.g. box supports mapping, but user has turned feature off)"
                            ,
                            "Network Failure (e.g. NAT box itself has not obtained a DHCP lease)",
                            "Out of resources (NAT box cannot create any more mappings at this time)",
                            "Unsupported opcode"
                        };

                        var errorMsg = errors[resultCode];
                        NatUtility.Log("Error in CreatePortMapListen: " + errorMsg);
                        return;
                    }

                    if (lifetime == 0)
                    {
                        return;                //mapping was deleted
                    }
                    //mapping was created
                    //TODO: verify that the private port+protocol are a match
                    mapping.PublicPort = publicPort;
                    mapping.Protocol   = protocol;
                    mapping.Expiration = DateTime.Now.AddSeconds(lifetime);
                    return;
                }
                catch (Exception ex)
                {
                    NatUtility.Logger.ErrorException("Error in CreatePortMapListen", ex);
                    return;
                }
            }
        }
Exemple #17
0
        async Task <UpnpNatDevice> ServicesReceived(IPAddress localAddress, Uri deviceServiceUri, HttpWebResponse response)
        {
            int abortCount = 0;

            byte []       buffer      = new byte [10240];
            StringBuilder servicesXml = new StringBuilder();
            XmlDocument   xmldoc      = new XmlDocument();
            Stream        s           = response.GetResponseStream();

            if (response.StatusCode != HttpStatusCode.OK)
            {
                NatUtility.Log("{0}: Couldn't get services list: {1}", response.ResponseUri, response.StatusCode);
                return(null);                // FIXME: This the best thing to do??
            }

            while (true)
            {
                var bytesRead = await s.ReadAsync(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 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);
                    }
                    NatUtility.Log("{0}: Couldn't parse services list", response.ResponseUri);
                    await Task.Delay(10);
                }
            }

            NatUtility.Log("{0}: Parsed services list", 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)
                {
                    //If the service is a WANIPConnection, then we have what we want
                    string serviceType = service ["serviceType"].InnerText;
                    NatUtility.Log("{0}: Found service: {1}", response.ResponseUri, serviceType);
                    StringComparison c = StringComparison.OrdinalIgnoreCase;
                    // TODO: Add support for version 2 of UPnP.
                    if (serviceType.Equals("urn:schemas-upnp-org:service:WANPPPConnection:1", c) ||
                        serviceType.Equals("urn:schemas-upnp-org:service:WANIPConnection:1", c))
                    {
                        var        controlUrl     = new Uri(service ["controlURL"].InnerText, UriKind.RelativeOrAbsolute);
                        IPEndPoint deviceEndpoint = new IPEndPoint(IPAddress.Parse(response.ResponseUri.Host), response.ResponseUri.Port);
                        NatUtility.Log("{0}: Found upnp service at: {1}", response.ResponseUri, controlUrl.OriginalString);
                        try {
                            if (controlUrl.IsAbsoluteUri)
                            {
                                deviceEndpoint = new IPEndPoint(IPAddress.Parse(controlUrl.Host), controlUrl.Port);
                                NatUtility.Log("{0}: New control url: {1}", deviceEndpoint, controlUrl);
                            }
                            else
                            {
                                controlUrl = new Uri(deviceServiceUri, controlUrl.OriginalString);
                            }
                        } catch {
                            controlUrl = new Uri(deviceServiceUri, controlUrl.OriginalString);
                            NatUtility.Log("{0}: Assuming control Uri is relative: {1}", deviceEndpoint, controlUrl);
                        }
                        NatUtility.Log("{0}: Handshake Complete", deviceEndpoint);
                        return(new UpnpNatDevice(localAddress, deviceEndpoint, controlUrl, serviceType));
                    }
                }
            }

            //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(null);
        }