protected void HandleNotifyRequest(SimpleHTTPRequest header, EndpointConfiguration config, IPEndPoint remoteEndPoint)
        {
            if (header.Param != "*")
            {
                // Invalid message
                return;
            }
            HTTPVersion httpVersion;

            if (!HTTPVersion.TryParse(header.HttpVersion, out httpVersion))
            {
                // Invalid message
                return;
            }
            // HOST not interesting
            //string host = header["HOST"];
            string cacheControl = header["CACHE-CONTROL"];
            string location     = header["LOCATION"];
            string server       = header["SERVER"];
            // NT is not evaluated, we get all information from the USN header
            //string nt = header["NT"];
            string nts = header["NTS"];
            string usn = header["USN"];
            string bi  = header["BOOTID.UPNP.ORG"];
            string ci  = header["CONFIGID.UPNP.ORG"];
            string sp  = header["SEARCHPORT.UPNP.ORG"];

            HandleNotifyPacket(config, remoteEndPoint, httpVersion, DateTime.Now.ToUniversalTime().ToString("R"), cacheControl, location, server, nts, usn, bi, ci, sp);
        }
Пример #2
0
        /// <summary>
        /// Sends a NOTIFY packet "ssdp:alive" to all UPnP endpoints.
        /// </summary>
        /// <param name="NT">Notification type.</param>
        /// <param name="USN">Unique Service Name.</param>
        /// <param name="rootDevice">Root device for that the message should be send.</param>
        public void SendMessage(string NT, string USN, DvDevice rootDevice)
        {
            SimpleHTTPRequest response = new SimpleHTTPRequest("NOTIFY", "*");

            response.SetHeader("CACHE-CONTROL", "max-age = " + _serverData.AdvertisementExpirationTime);
            response.SetHeader("NT", NT);
            response.SetHeader("NTS", "ssdp:alive");
            response.SetHeader("SERVER", UPnPConfiguration.UPnPMachineInfoHeader);
            response.SetHeader("USN", USN);
            response.SetHeader("BOOTID.UPNP.ORG", _serverData.BootId.ToString());
            // Currently, we don't support SEARCHPORT.UPNP.ORG function and header

            foreach (EndpointConfiguration config in _serverData.UPnPEndPoints)
            {
                if (config.AddressFamily == AddressFamily.InterNetworkV6)
                {
                    response.SetHeader("OPT", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01");
                    response.SetHeader("01-NLS", _serverData.BootId.ToString());
                }
                response.SetHeader("CONFIGID.UPNP.ORG", config.ConfigId.ToString());
                IPEndPoint ep = new IPEndPoint(config.SSDPMulticastAddress, UPnPConsts.SSDP_MULTICAST_PORT);
                response.SetHeader("HOST", NetworkHelper.IPEndPointToString(ep));
                if (config.SSDPUsesSpecialSearchPort)
                {
                    response.SetHeader("SEARCHPORT.UPNP.ORG", config.SSDPSearchPort.ToString());
                }
                response.SetHeader("LOCATION", config.GetRootDeviceDescriptionURL(rootDevice));
                byte[] bytes = response.Encode();
                NetworkHelper.MulticastMessage(config.SSDP_UDP_UnicastSocket, config.SSDPMulticastAddress, bytes);
            }
        }
        protected void UnicastSearchForST(string st, IPEndPoint endPoint)
        {
            SimpleHTTPRequest request = new SimpleHTTPRequest("M-SEARCH", "*");

            request.SetHeader("HOST", NetworkHelper.IPEndPointToString(endPoint));
            request.SetHeader("MAN", "\"ssdp:discover\"");
            request.SetHeader("ST", st);
            request.SetHeader("USER-AGENT", UPnPConfiguration.UPnPMachineInfoHeader);
            lock (_cpData.SyncObj)
            {
                foreach (EndpointConfiguration config in _cpData.Endpoints)
                {
                    if (config.AddressFamily != endPoint.AddressFamily)
                    {
                        continue;
                    }
                    Socket socket = config.SSDP_UDP_UnicastSocket;
                    if (socket == null)
                    {
                        return;
                    }
                    byte[] bytes = request.Encode();
                    NetworkHelper.SendData(socket, endPoint, bytes, 1); // The server will send the answer to the same socket as we use to send
                    return;
                }
            }
        }
        public void HandleMulticastEventNotification(SimpleHTTPRequest request)
        {
            string nt          = request["NT"];
            string nts         = request["NTS"];
            string sid         = request["SID"];
            string usn         = request["USN"];
            string svcid       = request["SVCID"];
            string contentType = request["CONTENT-TYPE"];

            lock (_cpData.SyncObj)
            {
                CpService service;
                if (nt != "upnp:event" || nts != "upnp:propchange" || string.IsNullOrEmpty(sid) ||
                    !TryGetService(usn, svcid, out service))
                {
                    return;
                }
                Encoding contentEncoding;
                string   mediaType;
                if (!EncodingUtils.TryParseContentTypeEncoding(contentType, Encoding.UTF8, out mediaType, out contentEncoding) ||
                    mediaType != "text/xml")
                {
                    return;
                }
                Stream stream = new MemoryStream(request.MessageBody);
                HandleEventNotification(stream, contentEncoding, service, _upnpVersion);
            }
        }
        protected void SendMulticastEventNotification(DvService service, IEnumerable <DvStateVariable> variables)
        {
            DvDevice      device        = service.ParentDevice;
            EventingState eventingState = _serverData.GetMulticastEventKey(service);
            // First cluster variables by multicast event level so we can put variables of the same event level into a single message
            IDictionary <string, ICollection <DvStateVariable> > variablesByLevel =
                new Dictionary <string, ICollection <DvStateVariable> >();

            foreach (DvStateVariable variable in variables)
            {
                ICollection <DvStateVariable> variablesCollection;
                if (!variablesByLevel.TryGetValue(variable.MulticastEventLevel, out variablesCollection))
                {
                    variablesByLevel[variable.MulticastEventLevel] = variablesCollection = new List <DvStateVariable>();
                }
                variablesCollection.Add(variable);
            }
            foreach (KeyValuePair <string, ICollection <DvStateVariable> > varByLevel in variablesByLevel)
            {
                // Use a maximum cluster size of GENA_MAX_MULTICAST_EVENT_VAR_COUNT to keep UDP message small
                ICollection <IList <DvStateVariable> > variableClusters = CollectionUtils.Cluster(
                    varByLevel.Value, UPnPConsts.GENA_MAX_MULTICAST_EVENT_VAR_COUNT);
                foreach (IList <DvStateVariable> cluster in variableClusters)
                {
                    foreach (DvStateVariable variable in cluster)
                    {
                        eventingState.UpdateModerationData(variable);
                    }
                    eventingState.IncEventKey();
                    byte[] bodyData = UPnPConsts.UTF8_NO_BOM.GetBytes(GENAMessageBuilder.BuildEventNotificationMessage(
                                                                          cluster, false)); // Albert TODO: Is it correct not to force the simple string equivalent for extended data types here?
                    SimpleHTTPRequest request = new SimpleHTTPRequest("NOTIFY", "*");
                    request.SetHeader("CONTENT-LENGTH", bodyData.Length.ToString());
                    request.SetHeader("CONTENT-TYPE", "text/xml; charset=\"utf-8\"");
                    request.SetHeader("USN", device.UDN + "::" + service.ServiceTypeVersion_URN);
                    request.SetHeader("SVCID", service.ServiceId);
                    request.SetHeader("NT", "upnp:event");
                    request.SetHeader("NTS", "upnp:propchange");
                    request.SetHeader("SEQ", eventingState.EventKey.ToString());
                    request.SetHeader("LVL", varByLevel.Key);
                    request.SetHeader("BOOTID.UPNP.ORG", _serverData.BootId.ToString());

                    foreach (EndpointConfiguration config in _serverData.UPnPEndPoints)
                    {
                        IPEndPoint ep = new IPEndPoint(config.GENAMulticastAddress, UPnPConsts.GENA_MULTICAST_PORT);
                        request.SetHeader("HOST", NetworkHelper.IPEndPointToString(ep));
                        request.MessageBody = bodyData;
                        byte[] bytes = request.Encode();
                        NetworkHelper.SendData(config.GENA_UDP_Socket, ep, bytes, 1);
                    }
                }
            }
        }
        protected void HandleSSDPRequest(SimpleHTTPRequest header, EndpointConfiguration config, IPEndPoint remoteEndPoint)
        {
            switch (header.Method)
            {
            case "NOTIFY":
                HandleNotifyRequest(header, config, remoteEndPoint);
                break;

            case "UPDATE":
                HandleUpdatePacket(header, config);
                break;
            }
        }
        private void OnSSDPMulticastReceive(IAsyncResult ar)
        {
            lock (_cpData.SyncObj)
                if (!_isActive)
                {
                    return;
                }
            UDPAsyncReceiveState <EndpointConfiguration> state = (UDPAsyncReceiveState <EndpointConfiguration>)ar.AsyncState;
            EndpointConfiguration config = state.Endpoint;
            Socket socket = config.SSDP_UDP_MulticastReceiveSocket;

            if (socket == null)
            {
                return;
            }
            try
            {
                EndPoint remoteEP = new IPEndPoint(state.Endpoint.AddressFamily == AddressFamily.InterNetwork ?
                                                   IPAddress.Any : IPAddress.IPv6Any, 0);
                using (Stream stream = new MemoryStream(state.Buffer, 0, socket.EndReceiveFrom(ar, ref remoteEP)))
                {
                    try
                    {
                        SimpleHTTPRequest header;
                        SimpleHTTPRequest.Parse(stream, out header);
                        HandleSSDPRequest(header, config, (IPEndPoint)remoteEP);
                    }
                    catch (Exception e)
                    {
                        UPnPConfiguration.LOGGER.Debug("SSDPClientController: Problem parsing incoming multicast UDP packet. Error message: '{0}'",
                                                       e.Message);
                    }
                }
                StartMulticastReceive(state);
            }
            catch (Exception) // SocketException, ObjectDisposedException
            {
                // Socket was closed - ignore this exception
                UPnPConfiguration.LOGGER.Info("SSDPClientController: Stopping listening for multicast messages at IP endpoint '{0}'",
                                              NetworkHelper.IPAddrToString(config.EndPointIPAddress));
            }
        }
Пример #8
0
        private void OnSSDPReceive(IAsyncResult ar)
        {
            lock (_serverData.SyncObj)
                if (!_serverData.IsActive)
                {
                    return;
                }
            UDPAsyncReceiveState <EndpointConfiguration> state = (UDPAsyncReceiveState <EndpointConfiguration>)ar.AsyncState;
            EndpointConfiguration config = state.Endpoint;
            Socket socket = state.Socket;

            try
            {
                EndPoint remoteEP = new IPEndPoint(
                    state.Endpoint.AddressFamily == AddressFamily.InterNetwork ?
                    IPAddress.Any : IPAddress.IPv6Any, 0);
                // To retrieve the remote endpoint address, it is necessary that the SocketOptionName.PacketInformation is set to true on the socket
                using (Stream stream = new MemoryStream(state.Buffer, 0, socket.EndReceiveFrom(ar, ref remoteEP)))
                {
                    try
                    {
                        SimpleHTTPRequest header;
                        SimpleHTTPRequest.Parse(stream, out header);
                        HandleSSDPRequest(header, config, (IPEndPoint)remoteEP);
                    }
                    catch (Exception e)
                    {
                        UPnPConfiguration.LOGGER.Debug(
                            "SSDPServerController: Problem parsing incoming packet at IP endpoint '{0}'. Error message: '{1}'", e,
                            NetworkHelper.IPAddrToString(config.EndPointIPAddress), e.Message);
                    }
                }
                StartReceive(state);
            }
            catch (Exception) // SocketException, ObjectDisposedException
            {
                // Socket was closed - ignore this exception
                UPnPConfiguration.LOGGER.Info("SSDPServerController: Stopping listening for multicast messages at IP endpoint '{0}'",
                                              NetworkHelper.IPAddrToString(config.EndPointIPAddress));
            }
        }
Пример #9
0
        private static void Parse(List <string> requestHeaders)
        {
            // Construct header using different line break styles
            IEnumerable <string> delimiters = new[] { "\r\n", "\n" };

            foreach (string delimiter in delimiters)
            {
                string fullRequest = string.Join(delimiter, requestHeaders.ToArray());

                using (Stream stream = new MemoryStream(Encoding.UTF8.GetBytes(fullRequest)))
                {
                    SimpleHTTPRequest result;
                    SimpleHTTPRequest.Parse(stream, out result);
                    for (int index = 1; index < requestHeaders.Count - 2; index++)
                    {
                        string requestHeader = requestHeaders[index];
                        int    pos           = requestHeader.IndexOf(':');
                        string header        = requestHeader.Substring(0, pos).ToUpperInvariant();
                        string value         = requestHeader.Substring(pos + 1).Trim();
                        Assert.AreEqual(value, result[header]);
                    }
                }
            }
        }
        protected void MulticastSearchForST(string st)
        {
            SimpleHTTPRequest request = new SimpleHTTPRequest("M-SEARCH", "*");

            request.SetHeader("MAN", "\"ssdp:discover\"");
            request.SetHeader("MX", UPnPConfiguration.SEARCH_MX.ToString());
            request.SetHeader("ST", st);
            request.SetHeader("USER-AGENT", UPnPConfiguration.UPnPMachineInfoHeader);
            lock (_cpData.SyncObj)
            {
                foreach (EndpointConfiguration config in _cpData.Endpoints)
                {
                    IPEndPoint ep = new IPEndPoint(config.SSDPMulticastAddress, UPnPConsts.SSDP_MULTICAST_PORT);
                    request.SetHeader("HOST", NetworkHelper.IPEndPointToString(ep));
                    Socket socket = config.SSDP_UDP_UnicastSocket;
                    if (socket == null)
                    {
                        continue;
                    }
                    byte[] bytes = request.Encode();
                    NetworkHelper.MulticastMessage(socket, config.SSDPMulticastAddress, bytes); // The server will send the answer to the same socket as we use to send
                }
            }
        }
Пример #11
0
        /// <summary>
        /// Sends a NOTIFY packet "ssdp:byebye" to all UPnP endpoints.
        /// </summary>
        /// <param name="NT">Notification type.</param>
        /// <param name="USN">Unique Service Name.</param>
        /// <param name="rootDevice">Root device for that the message should be send.</param>
        public void SendMessage(string NT, string USN, DvDevice rootDevice)
        {
            SimpleHTTPRequest response = new SimpleHTTPRequest("NOTIFY", "*");

            response.SetHeader("NT", NT);
            response.SetHeader("NTS", "ssdp:byebye");
            response.SetHeader("USN", USN);
            response.SetHeader("BOOTID.UPNP.ORG", _serverData.BootId.ToString());
            // Currently, we don't support SEARCHPORT.UPNP.ORG function and header

            foreach (EndpointConfiguration config in _serverData.UPnPEndPoints)
            {
                if (config.AddressFamily == AddressFamily.InterNetworkV6)
                {
                    response.SetHeader("OPT", "\"http://schemas.upnp.org/upnp/1/0/\"; ns=01");
                    response.SetHeader("01-NLS", _serverData.BootId.ToString());
                }
                response.SetHeader("CONFIGID.UPNP.ORG", config.ConfigId.ToString());
                IPEndPoint ep = new IPEndPoint(config.SSDPMulticastAddress, UPnPConsts.SSDP_MULTICAST_PORT);
                response.SetHeader("HOST", NetworkHelper.IPEndPointToString(ep));
                byte[] bytes = response.Encode();
                NetworkHelper.MulticastMessage(config.SSDP_UDP_UnicastSocket, config.SSDPMulticastAddress, bytes);
            }
        }
Пример #12
0
 /// <summary>
 /// Handles SSDP M-SEARCH requests over UDP multicast and unicast.
 /// </summary>
 /// <param name="header">HTTP request header of the request to handle.</param>
 /// <param name="config">Endpoint configuration which received the request.</param>
 /// <param name="remoteEP">Remote endpoint which sent the request.</param>
 protected void HandleSSDPRequest(SimpleHTTPRequest header, EndpointConfiguration config, IPEndPoint remoteEP)
 {
     switch (header.Method)
     {
     case "M-SEARCH":
         if (header.Param != "*" || header["MAN"] != "\"ssdp:discover\"")
         {
             throw new InvalidDataException("Unsupported Request");
         }
         // We don't make a difference between multicast and unicast search requests here,
         // the only difference is the existance of the MX header field.
         // If it is present, we simply use this random delay for the answer, and if it is
         // not present, we send the answer at once.
         int mx; // Max. seconds to delay response
         if (!int.TryParse(header["MX"], out mx))
         {
             mx = 0;
         }
         else if (mx < 1)
         {
             // Malformed request
             throw new InvalidDataException("Invalid MX header value");
         }
         if (mx > 5)
         {
             mx = 5;                              // Should be bounded to 5, according to (DevArch)
         }
         string st = header["ST"];                // Search target
         if (header.ContainsHeader("USER-AGENT")) // Optional
         {
             CheckUserAgentUPnPVersion(header["USER-AGENT"]);
         }
         DelaySearchResponse(new PendingSearchRequest(st, config, remoteEP), mx);
         break;
     }
 }
        protected void HandleUpdatePacket(SimpleHTTPRequest header, EndpointConfiguration config)
        {
            if (header.Param != "*")
            {
                // Invalid message
                return;
            }
            HTTPVersion httpVersion;

            if (!HTTPVersion.TryParse(header.HttpVersion, out httpVersion))
            {
                // Invalid message
                return;
            }
            // Host, NT, NTS, USN are not interesting
            //string host = header["HOST"];
            //string nt = header["NT"];
            //string nts = header["NTS"];
            string usn = header["USN"];
            //string location = header["LOCATION"];
            string bi = header["BOOTID.UPNP.ORG"];
            uint   bootID;

            if (!uint.TryParse(bi, out bootID))
            {
                // Invalid message
                return;
            }
            string nbi = header["NEXTBOOTID.UPNP.ORG"];
            uint   nextBootID;

            if (!uint.TryParse(nbi, out nextBootID))
            {
                // Invalid message
                return;
            }
            if (!usn.StartsWith("uuid:"))
            {
                // Invalid usn
                return;
            }
            int separatorIndex = usn.IndexOf("::");

            if (separatorIndex < 6) // separatorIndex == -1 or separatorIndex not after "uuid:" prefix with at least one char UUID
            // We only use messages containing a "::" substring and discard the "uuid:device-UUID" message
            {
                return;
            }
            string    deviceUUID = usn.Substring(5, separatorIndex - 5);
            RootEntry rootEntry  = GetRootEntryByContainedDeviceUUID(deviceUUID);

            if (rootEntry == null)
            {
                return;
            }
            if (rootEntry.BootID > bootID)
            {
                // Invalid message
                return;
            }
            bool fireDeviceRebooted = false;

            lock (_cpData.SyncObj)
            {
                if (rootEntry.BootID < bootID)
                {
                    // Device reboot
                    fireDeviceRebooted = true;
                }
                rootEntry.BootID = nextBootID;
            }
            if (fireDeviceRebooted)
            {
                InvokeDeviceRebooted(rootEntry, false);
            }
        }