} // Discover public void OnReceive(IAsyncResult ar) // a reply has arrived... { UdpClient client = (UdpClient)ar.AsyncState; IPEndPoint ep = null; byte[] buf = client.EndReceive(ar, ref ep); // get the response if (buf != null) { client.BeginReceive(new AsyncCallback(OnReceive), client); // set up for another message to arrive try { HTTPMessage msg = HTTPMessage.ParseByteArray(buf, 0, buf.Length); // here filter to replies containing 'sonos' and add to zones.. if (msg.StringPacket.ToLower().Contains("sonos")) { string loc = msg.GetTag("Location"); if (!_zones.Contains(loc)) { _zones.Add(loc); _zoneTable.Add(loc, ""); // Will fill in zone friendly name later. } } } catch { // a problem dealing with the reply message, so ignore it } return; } } // OnReceive
/// <summary> /// Sends a UDP package over the specified broadcast IP and port and waits for responses. /// Some background: http://www.upnp-hacks.org/upnp.html, http://upnp.org/specs/arch/UPnP-arch-DeviceArchitecture-v1.0.pdf /// Refactored May 2016 Chris Fox, using code/structures from DeviceSpy - see http://www.meshcommander.com/upnptools /// </summary> /// public void Discover() // this cannot be a static class because it uses async callbacks { // create the discovery message to broadcast.. HTTPMessage request = new HTTPMessage(); IPEndPoint RemoteEP = new IPEndPoint(IPAddress.Parse(_broadcastIP), _broadcastPort); request.Directive = "M-SEARCH"; request.DirectiveObj = "*"; request.AddTag("ST", _searchTarget); request.AddTag("MX", "2"); request.AddTag("MAN", "\"ssdp:discover\""); request.AddTag("HOST", RemoteEP.ToString()); // "239.255.255.250:1900" byte[] buffer = UTF8Encoding.UTF8.GetBytes(request.StringPacket); // get the local network interfaces... we will braodcast and receive responses on each interface we find... ArrayList AddressTable = new ArrayList(); Hashtable Sessions = new Hashtable(); // used to keep track of reponses NetworkInterface[] interfaces = NetworkInterface.GetAllNetworkInterfaces(); foreach (NetworkInterface i in interfaces) { if (i.IsReceiveOnly == false && i.OperationalStatus == OperationalStatus.Up && i.SupportsMulticast == true) { IPInterfaceProperties i2 = i.GetIPProperties(); foreach (UnicastIPAddressInformation i3 in i2.UnicastAddresses) { if (!AddressTable.Contains(i3.Address) && !i3.Address.Equals(IPAddress.IPv6Loopback) && !i3.Address.Equals(IPAddress.Loopback)) { AddressTable.Add(i3.Address); } } } } // AddressTable now contains a list of non-loopback network interfaces that are up //Now broadcast via each of them.. foreach (IPAddress localaddr in AddressTable) { // set up a listener... // Look up in Sessions keyed by local addr - if not found, create and add UdpClient cl = (UdpClient)Sessions[localaddr]; // get previusly-used client if (cl == null) { cl = new UdpClient(new IPEndPoint(localaddr, 0)); // make a new one - bound to local address and random port cl.EnableBroadcast = true; cl.BeginReceive(new AsyncCallback(OnReceive), cl); // set up an async receive, pass in the UDP Client details Sessions[localaddr] = cl; } if (RemoteEP.AddressFamily != cl.Client.AddressFamily) { continue; } if ((RemoteEP.AddressFamily == AddressFamily.InterNetworkV6) && ((IPEndPoint)cl.Client.LocalEndPoint).Address.IsIPv6LinkLocal == true && RemoteEP != OpenSource.UPnP.Utils.UpnpMulticastV6EndPoint2) { continue; } if ((RemoteEP.AddressFamily == AddressFamily.InterNetworkV6) && ((IPEndPoint)cl.Client.LocalEndPoint).Address.IsIPv6LinkLocal == false && RemoteEP != OpenSource.UPnP.Utils.UpnpMulticastV6EndPoint1) { continue; } IPEndPoint lep = (IPEndPoint)cl.Client.LocalEndPoint; if (cl.Client.AddressFamily == AddressFamily.InterNetwork) { cl.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, localaddr.GetAddressBytes()); } else if (cl.Client.AddressFamily == AddressFamily.InterNetworkV6) { cl.Client.SetSocketOption(SocketOptionLevel.IPv6, SocketOptionName.MulticastInterface, BitConverter.GetBytes((int)localaddr.ScopeId)); } cl.Send(buffer, buffer.Length, RemoteEP); // and do the broadcast } // now wait a little while for the replies to come back... System.Threading.Thread.Sleep(_timeout); } // Discover
/// <summary> /// Parses a Byte Array at a specific location, and builds a Packet. /// </summary> /// <param name="buffer">The Array of Bytes</param> /// <param name="indx">The Start Index</param> /// <param name="count">The number of Bytes to process</param> /// <returns></returns> static public HTTPMessage ParseByteArray(byte[] buffer, int indx, int count) { HTTPMessage TheMessage = new HTTPMessage(); UTF8Encoding UTF8 = new UTF8Encoding(); String TempData = UTF8.GetString(buffer, indx, count); DText parser = new DText(); String TempString; int idx = TempData.IndexOf("\r\n\r\n"); if (idx < 0) { return(null); } TempData = TempData.Substring(0, idx); parser.ATTRMARK = "\r\n"; parser.MULTMARK = ":"; parser[0] = TempData; string CurrentLine = parser[1]; DText HdrParser = new DText(); HdrParser.ATTRMARK = " "; HdrParser.MULTMARK = "/"; HdrParser[0] = CurrentLine; if (CurrentLine.ToUpper().StartsWith("HTTP/") == true) { TheMessage.ResponseCode = int.Parse(HdrParser[2]); int s1 = CurrentLine.IndexOf(" "); s1 = CurrentLine.IndexOf(" ", s1 + 1); TheMessage.ResponseData = HTTPMessage.UnEscapeString(CurrentLine.Substring(s1)); try { TheMessage.Version = HdrParser[1, 2]; } catch (Exception ex) { //OpenSource.Utilities.EventLogger.Log(ex); TheMessage.Version = "0.9"; } } else { TheMessage.Directive = HdrParser[1]; TempString = CurrentLine.Substring(CurrentLine.LastIndexOf(" ") + 1); if (TempString.ToUpper().StartsWith("HTTP/") == false) { TheMessage.Version = "0.9"; TheMessage.DirectiveObj = HTTPMessage.UnEscapeString(TempString); } else { TheMessage.Version = TempString.Substring(TempString.IndexOf("/") + 1); int fs = CurrentLine.IndexOf(" ") + 1; TheMessage.DirectiveObj = HTTPMessage.UnEscapeString(CurrentLine.Substring( fs, CurrentLine.Length - fs - TempString.Length - 1)); } } String Tag = ""; String TagData = ""; for (int line = 2; line <= parser.DCOUNT(); ++line) { if (Tag != "" && parser[line, 1].StartsWith(" ")) { TagData = parser[line, 1].Substring(1); } else { Tag = parser[line, 1]; TagData = ""; for (int i = 2; i <= parser.DCOUNT(line); ++i) { if (TagData == "") { TagData = parser[line, i]; } else { TagData = TagData + parser.MULTMARK + parser[line, i]; } } } TheMessage.AppendTag(Tag, TagData); } int cl = 0; if (TheMessage.HasTag("Content-Length")) { try { cl = int.Parse(TheMessage.GetTag("Content-Length")); } catch (Exception ex) { //OpenSource.Utilities.EventLogger.Log(ex); cl = -1; } } else { cl = -1; } byte[] tbuffer; if (cl > 0) { tbuffer = new byte[cl]; if ((idx + 4 + cl) > count) { // NOP } else { Array.Copy(buffer, idx + 4, tbuffer, 0, cl); TheMessage.DataBuffer = tbuffer; } } if (cl == -1) { tbuffer = new Byte[count - (idx + 4)]; Array.Copy(buffer, idx + 4, tbuffer, 0, tbuffer.Length); TheMessage.DataBuffer = tbuffer; } if (cl == 0) { TheMessage.DataBuffer = new byte[0]; } return(TheMessage); }