예제 #1
0
 public static bool TryParse(string versionStr, out HTTPVersion result)
 {
   result = null;
   int dotIndex = versionStr.IndexOf('.');
   if (!versionStr.StartsWith(VERSION_PREFIX) || dotIndex < VERSION_PREFIX.Length + 1)
     return false;
   int verMin;
   if (!int.TryParse(versionStr.Substring(VERSION_PREFIX.Length, dotIndex - VERSION_PREFIX.Length), out verMin))
     return false;
   int verMax;
   if (!int.TryParse(versionStr.Substring(dotIndex + 1), out verMax))
     return false;
   result = new HTTPVersion(verMax, verMin);
   return true;
 }
예제 #2
0
        public static bool TryParse(string versionStr, out HTTPVersion result)
        {
            result = null;
            int dotIndex = versionStr.IndexOf('.');

            if (!versionStr.StartsWith(VERSION_PREFIX) || dotIndex < VERSION_PREFIX.Length + 1)
            {
                return(false);
            }
            int verMin;

            if (!int.TryParse(versionStr.Substring(VERSION_PREFIX.Length, dotIndex - VERSION_PREFIX.Length), out verMin))
            {
                return(false);
            }
            int verMax;

            if (!int.TryParse(versionStr.Substring(dotIndex + 1), out verMax))
            {
                return(false);
            }
            result = new HTTPVersion(verMax, verMin);
            return(true);
        }
예제 #3
0
    protected void HandleNotifyPacket(EndpointConfiguration config, IPEndPoint remoteEndPoint, HTTPVersion httpVersion,
        string date, string cacheControl, string location, string server, string nts, string usn, string bi, string ci, string sp)
    {
      uint bootID = 0;
      if (bi != null && !uint.TryParse(bi, out bootID))
        // Invalid message
        return;
      uint configID = 0;
      if (ci != null && !uint.TryParse(ci, out configID))
        // Invalid message
        return;
      if (!usn.StartsWith("uuid:"))
        // Invalid usn
        return;
      string deviceUUID;
      string messageType;
      if (!ParserHelper.TryParseUSN(usn, out deviceUUID, out messageType))
        // We only use messages of the type "uuid:device-UUID::..." and discard the "uuid:device-UUID" message
        return;
      if (nts == "ssdp:alive")
      {
        if (server == null)
          // Invalid message
          return;
        int maxAge;
        if (!TryParseMaxAge(cacheControl, out maxAge))
          // Invalid message
          return;
        DateTime d;
        if (!DateTime.TryParse(date, out d))
          d = DateTime.Now;
        DateTime expirationTime = d.AddSeconds(maxAge);
        // The specification says the SERVER header should contain three entries, separated by space, like
        // "SERVER: OS/version UPnP/1.1 product/version".
        // Unfortunately, some clients send entries separated by ", ", like "Linux/2.x.x, UPnP/1.0, pvConnect UPnP SDK/1.0".
        // We try to handle all situations correctly here, that's the reason for this ugly code.

        // What we've seen until now:
        // SERVER: Linux/2.x.x, UPnP/1.0, pvConnect UPnP SDK/1.0  => tokens separated by ','
        // SERVER: Windows 2003, UPnP/1.0 DLNADOC/1.50, Serviio/0.5.2  => tokens separated by ',' and additional info in UPnP version token
        // SERVER: 3Com-ADSL-11g/1.0 UPnP/1.0  => only two tokens
        string[] versionInfos = server.Contains(", ") ? server.Split(new string[] { ", " }, StringSplitOptions.RemoveEmptyEntries) :
            server.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
        string upnpVersionInfo = versionInfos.FirstOrDefault(v => v.StartsWith(UPnPVersion.VERSION_PREFIX));
        if (upnpVersionInfo == null)
          // Invalid message
          return;
        // upnpVersionInfo = 'UPnP/1.0', 'UPnP/1.1', 'UPnP/1.0 DLNADOC/1.50', ..., the UPnP version is always the first token
        string[] upnpVersionInfoTokens = upnpVersionInfo.Split(' ');
        string upnpVersionInfoToken = upnpVersionInfoTokens[0];
        UPnPVersion upnpVersion;
        if (!UPnPVersion.TryParse(upnpVersionInfoToken, out upnpVersion))
          // Invalid message
          return;
        if (upnpVersion.VerMax != 1)
          // Incompatible UPnP version
          return;
        int searchPort = 1900;
        if (upnpVersion.VerMin >= 1)
        {
          if (bi == null || ci == null)
            // Invalid message
            return;
          if (sp != null && (!int.TryParse(sp, out searchPort) || searchPort < 49152 || searchPort > 65535))
            // Invalid message
            return;
        }
        RootEntry rootEntry;
        DeviceEntry deviceEntry = null;
        string serviceType = null;
        bool fireDeviceRebooted = false;
        bool fireConfigurationChanged = false;
        bool fireRootDeviceAdded = false;
        bool fireDeviceAdded = false;
        bool fireServiceAdded = false;
        lock (_cpData.SyncObj)
        {
          bool rootEntryAdded;
          // Use fail-safe code, see comment above about the different SERVER headers
          string osVersion = versionInfos.Length < 1 ? string.Empty : versionInfos[0];
          string productVersion = versionInfos.Length < 3 ? string.Empty : versionInfos[2];
          rootEntry = GetOrCreateRootEntry(deviceUUID, location, upnpVersion, osVersion,
              productVersion, expirationTime, config, httpVersion, searchPort, out rootEntryAdded);
          if (bi != null && rootEntry.BootID > bootID)
            // Invalid message
            return;
          uint currentConfigId = rootEntry.GetConfigID(remoteEndPoint);
          if (currentConfigId != 0 && currentConfigId != configID)
            fireConfigurationChanged = true;
          rootEntry.SetConfigID(remoteEndPoint, configID);
          if (!rootEntryAdded && bi != null && rootEntry.BootID < bootID)
          { // Device reboot
            // A device, which has rebooted, has lost all its links, so we must forget about the old link registrations and wait for new registrations in alive messages
            rootEntry.ClearLinks();
            fireDeviceRebooted = true;
          }
          // Don't add the link before a reboot was detected and thus, rootEntry.ClearLinks() was called
          rootEntry.AddOrUpdateLink(config, location, httpVersion, searchPort);
          rootEntry.BootID = bootID;
          if (messageType == "upnp:rootdevice")
          {
            rootEntry.GetOrCreateDeviceEntry(deviceUUID);
            object value;
            if (!rootEntry.ClientProperties.TryGetValue("RootDeviceSetUp", out value))
            {
              rootEntry = MergeOrMoveRootEntry(rootEntry, deviceUUID);
              fireRootDeviceAdded = true;
              rootEntry.ClientProperties["RootDeviceSetUp"] = true;
            }
          }
          else if (messageType.StartsWith("urn:"))
          {
            if (messageType.IndexOf(":device:") > -1)
            {
              string deviceType;
              int deviceTypeVersion;
              if (!ParserHelper.TryParseTypeVersion_URN(messageType, out deviceType, out deviceTypeVersion))
                // Invalid message
                return;
              deviceEntry = rootEntry.GetOrCreateDeviceEntry(deviceUUID);
              fireDeviceAdded = string.IsNullOrEmpty(deviceEntry.DeviceType);
              deviceEntry.DeviceType = deviceType;
              deviceEntry.DeviceTypeVersion = deviceTypeVersion;
            }
            else if (messageType.IndexOf(":service:") > -1)
            {
              deviceEntry = rootEntry.GetOrCreateDeviceEntry(deviceUUID);
              serviceType = messageType;
              if (deviceEntry.Services.Contains(serviceType))
                return;
              deviceEntry.Services.Add(serviceType);
              fireServiceAdded = true;
            }
          }
          else
            // Invalid message
            return;
        }
        // Raise events after returning the lock
        if (fireDeviceRebooted)
          InvokeDeviceRebooted(rootEntry, fireConfigurationChanged);
        else if (fireConfigurationChanged)
          InvokeDeviceConfigurationChanged(rootEntry);
        if (fireRootDeviceAdded)
          InvokeRootDeviceAdded(rootEntry);
        if (fireDeviceAdded)
          InvokeDeviceAdded(rootEntry, deviceEntry);
        if (fireServiceAdded)
          InvokeServiceAdded(rootEntry, deviceEntry, serviceType);
      }
      else if (nts == "ssdp:byebye")
      {
        RootEntry rootEntry = GetRootEntryByContainedDeviceUUID(deviceUUID);
        if (rootEntry != null)
        {
          if (bi != null && rootEntry.BootID > bootID)
            // Invalid message
            return;
          RemoveRootEntry(rootEntry);
          InvokeRootDeviceRemoved(rootEntry);
        }
      }
    }
예제 #4
0
 protected RootEntry GetOrCreateRootEntry(string deviceUUID, string descriptionLocation, UPnPVersion upnpVersion, string osVersion,
     string productVersion, DateTime expirationTime, EndpointConfiguration endpoint, HTTPVersion httpVersion, int searchPort, out bool wasAdded)
 {
   // Because the order of the UDP advertisement packets isn't guaranteed (and even not really specified by the UPnP specification),
   // in the general case it is not possible to find the correct root entry for each advertisement message.
   // - We cannot search by root device UUID because the upnp:rootdevice message might not be the first message, so before that message, we don't know the root device ID and
   //   thus we cannot use the root device id as unique key to find the root entry
   // - We cannot use the device description because for multi-homed devices, more than one device description can belong to the same root device
   //
   // Assume the message arrive in an order so that device A over network interface N1 is announced first. Second, device B over network interface N2 is announced.
   // In that case, we cannot judge if those two devices belong to the same root device or not.
   //
   // To face that situation, we first add all advertised devices to _pendingDeviceEntries. When a upnp:rootdevice message is received,
   // we either simply move the root entry from _pendingDeviceEntries into _cpData.DeviceEntries or we merge the pending entry with an already existing
   // entry in _cpData.DeviceEntries. At that time the merge is possible because we then have the root device id for both root entries.
   lock (_cpData.SyncObj)
   {
     RootEntry result = GetRootEntryByContainedDeviceUUID(deviceUUID) ?? GetRootEntryByDescriptionLocation(descriptionLocation);
     if (result != null)
     {
       result.ExpirationTime = expirationTime;
       wasAdded = false;
     }
     else
     {
       result = new RootEntry(_cpData.SyncObj, upnpVersion, osVersion, productVersion, expirationTime);
       _pendingDeviceEntries.Add(result);
       wasAdded = true;
     }
     return result;
   }
 }