SsdpHandler.cs 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260
  1. using MediaBrowser.Controller.Configuration;
  2. using MediaBrowser.Model.Logging;
  3. using System;
  4. using System.Collections.Generic;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Sockets;
  9. using System.Text;
  10. namespace MediaBrowser.Dlna.Server
  11. {
  12. public class SsdpHandler : IDisposable
  13. {
  14. private readonly ILogger _logger;
  15. private readonly IServerConfigurationManager _config;
  16. private readonly string _serverSignature;
  17. private bool _isDisposed = false;
  18. const string SSDPAddr = "239.255.255.250";
  19. const int SSDPPort = 1900;
  20. private readonly IPEndPoint _ssdpEndp = new IPEndPoint(IPAddress.Parse(SSDPAddr), SSDPPort);
  21. private readonly IPAddress _ssdpIp = IPAddress.Parse(SSDPAddr);
  22. private UdpClient _udpClient;
  23. private readonly Dictionary<Guid, List<UpnpDevice>> _devices = new Dictionary<Guid, List<UpnpDevice>>();
  24. public SsdpHandler(ILogger logger, IServerConfigurationManager config, string serverSignature)
  25. {
  26. _logger = logger;
  27. _config = config;
  28. _serverSignature = serverSignature;
  29. Start();
  30. }
  31. private IEnumerable<UpnpDevice> Devices
  32. {
  33. get
  34. {
  35. UpnpDevice[] devs;
  36. lock (_devices)
  37. {
  38. devs = _devices.Values.SelectMany(i => i).ToArray();
  39. }
  40. return devs;
  41. }
  42. }
  43. private void Start()
  44. {
  45. _udpClient = new UdpClient();
  46. _udpClient.Client.UseOnlyOverlappedIO = true;
  47. _udpClient.Client.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
  48. _udpClient.ExclusiveAddressUse = false;
  49. _udpClient.Client.Bind(new IPEndPoint(IPAddress.Any, SSDPPort));
  50. _udpClient.JoinMulticastGroup(_ssdpIp, 2);
  51. _logger.Info("SSDP service started");
  52. Receive();
  53. }
  54. private void Receive()
  55. {
  56. try
  57. {
  58. _udpClient.BeginReceive(ReceiveCallback, null);
  59. }
  60. catch (ObjectDisposedException)
  61. {
  62. }
  63. }
  64. private void ReceiveCallback(IAsyncResult result)
  65. {
  66. try
  67. {
  68. var endpoint = new IPEndPoint(IPAddress.None, SSDPPort);
  69. var received = _udpClient.EndReceive(result, ref endpoint);
  70. if (_config.Configuration.DlnaOptions.EnableDebugLogging)
  71. {
  72. _logger.Debug("{0} - SSDP Received a datagram", endpoint);
  73. }
  74. using (var reader = new StreamReader(new MemoryStream(received), Encoding.ASCII))
  75. {
  76. var proto = (reader.ReadLine() ?? string.Empty).Trim();
  77. var method = proto.Split(new[] { ' ' }, 2)[0];
  78. var headers = new Headers();
  79. for (var line = reader.ReadLine(); line != null; line = reader.ReadLine())
  80. {
  81. line = line.Trim();
  82. if (string.IsNullOrEmpty(line))
  83. {
  84. break;
  85. }
  86. var parts = line.Split(new[] { ':' }, 2);
  87. headers[parts[0]] = parts[1].Trim();
  88. }
  89. if (_config.Configuration.DlnaOptions.EnableDebugLogging)
  90. {
  91. _logger.Debug("{0} - Datagram method: {1}", endpoint, method);
  92. //_logger.Debug(headers);
  93. }
  94. if (string.Equals(method, "M-SEARCH", StringComparison.OrdinalIgnoreCase))
  95. {
  96. RespondToSearch(endpoint, headers["st"]);
  97. }
  98. }
  99. }
  100. catch (Exception ex)
  101. {
  102. _logger.ErrorException("Failed to read SSDP message", ex);
  103. }
  104. if (!_isDisposed)
  105. {
  106. Receive();
  107. }
  108. }
  109. private void RespondToSearch(IPEndPoint endpoint, string req)
  110. {
  111. if (req == "ssdp:all")
  112. {
  113. req = null;
  114. }
  115. if (_config.Configuration.DlnaOptions.EnableDebugLogging)
  116. {
  117. _logger.Debug("RespondToSearch");
  118. }
  119. foreach (var d in Devices)
  120. {
  121. if (!string.IsNullOrEmpty(req) && req != d.Type)
  122. {
  123. continue;
  124. }
  125. SendSearchResponse(endpoint, d);
  126. }
  127. }
  128. private void SendSearchResponse(IPEndPoint endpoint, UpnpDevice dev)
  129. {
  130. var headers = new Headers(true);
  131. headers.Add("CACHE-CONTROL", "max-age = 600");
  132. headers.Add("DATE", DateTime.Now.ToString("R"));
  133. headers.Add("EXT", "");
  134. headers.Add("LOCATION", dev.Descriptor.ToString());
  135. headers.Add("SERVER", _serverSignature);
  136. headers.Add("ST", dev.Type);
  137. headers.Add("USN", dev.USN);
  138. SendDatagram(endpoint, String.Format("HTTP/1.1 200 OK\r\n{0}\r\n", headers.HeaderBlock), false);
  139. _logger.Info("{1} - Responded to a {0} request", dev.Type, endpoint);
  140. }
  141. private void SendDatagram(IPEndPoint endpoint, string msg, bool sticky)
  142. {
  143. if (_isDisposed)
  144. {
  145. return;
  146. }
  147. //var dgram = new Datagram(endpoint, msg, sticky);
  148. //if (messageQueue.Count == 0)
  149. //{
  150. // dgram.Send();
  151. //}
  152. //messageQueue.Enqueue(dgram);
  153. //queueTimer.Enabled = true;
  154. }
  155. private void NotifyAll()
  156. {
  157. _logger.Debug("NotifyAll");
  158. foreach (var d in Devices)
  159. {
  160. NotifyDevice(d, "alive", false);
  161. }
  162. }
  163. private void NotifyDevice(UpnpDevice dev, string type, bool sticky)
  164. {
  165. _logger.Debug("NotifyDevice");
  166. var headers = new Headers(true);
  167. headers.Add("HOST", "239.255.255.250:1900");
  168. headers.Add("CACHE-CONTROL", "max-age = 600");
  169. headers.Add("LOCATION", dev.Descriptor.ToString());
  170. headers.Add("SERVER", _serverSignature);
  171. headers.Add("NTS", "ssdp:" + type);
  172. headers.Add("NT", dev.Type);
  173. headers.Add("USN", dev.USN);
  174. SendDatagram(_ssdpEndp, String.Format("NOTIFY * HTTP/1.1\r\n{0}\r\n", headers.HeaderBlock), sticky);
  175. _logger.Debug("{0} said {1}", dev.USN, type);
  176. }
  177. private void RegisterNotification(Guid UUID, Uri Descriptor)
  178. {
  179. List<UpnpDevice> list;
  180. lock (_devices)
  181. {
  182. if (!_devices.TryGetValue(UUID, out list))
  183. {
  184. _devices.Add(UUID, list = new List<UpnpDevice>());
  185. }
  186. }
  187. foreach (var t in new[] { "upnp:rootdevice", "urn:schemas-upnp-org:device:MediaServer:1", "urn:schemas-upnp-org:service:ContentDirectory:1", "uuid:" + UUID })
  188. {
  189. list.Add(new UpnpDevice(UUID, t, Descriptor));
  190. }
  191. NotifyAll();
  192. _logger.Debug("Registered mount {0}", UUID);
  193. }
  194. internal void UnregisterNotification(Guid UUID)
  195. {
  196. List<UpnpDevice> dl;
  197. lock (_devices)
  198. {
  199. if (!_devices.TryGetValue(UUID, out dl))
  200. {
  201. return;
  202. }
  203. _devices.Remove(UUID);
  204. }
  205. foreach (var d in dl)
  206. {
  207. NotifyDevice(d, "byebye", true);
  208. }
  209. _logger.Debug("Unregistered mount {0}", UUID);
  210. }
  211. public void Dispose()
  212. {
  213. _isDisposed = true;
  214. //while (messageQueue.Count != 0)
  215. //{
  216. // datagramPosted.WaitOne();
  217. //}
  218. _udpClient.DropMulticastGroup(_ssdpIp);
  219. _udpClient.Close();
  220. //notificationTimer.Enabled = false;
  221. //queueTimer.Enabled = false;
  222. //notificationTimer.Dispose();
  223. //queueTimer.Dispose();
  224. //datagramPosted.Dispose();
  225. }
  226. }
  227. }