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