"belltown" wrote:
One thing I wasn't really clear about was the "MX" header; I wasn't sure whether to leave it out, or if using it, what value to use for it. Maybe his devices are taking longer to respond to the SSDP requests, due to the number of devices, or network configuration, or something.
I did notice during my own testing that there were times when one or more Rokus did not always respond to SSDP requests. I'd try again later then every device responded.
"belltown" wrote:
It works fine discovering my Rokus, and I know at least one other person whose Rokus were also discovered. So far, TheEndless is the only one who's reported a problem with the discovery mechanism.
private string[] getRokuIPAddresses(int timeout = 5000)
{
List<string> ips = new List<string>();
IPAddress localNetwork = Dns.GetHostAddresses(Environment.GetEnvironmentVariable("COMPUTERNAME")).Where(ia => (ia.AddressFamily == AddressFamily.InterNetwork)).First();
IPEndPoint localEndPoint = new IPEndPoint(localNetwork, 0);
IPEndPoint multicastEndPoint = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900);
using (Socket udpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp))
{
udpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
udpSocket.Bind(localEndPoint);
udpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, new MulticastOption(multicastEndPoint.Address, IPAddress.Any));
udpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 2);
udpSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastLoopback, true);
Console.WriteLine("UDP socket setup done...\r\n");
string searchString = "M-SEARCH * HTTP/1.1\r\nHOST:239.255.255.250:1900\r\nMAN:\"ssdp:discover\"\r\nST:roku:ecp\r\n\r\n";
udpSocket.SendTo(Encoding.UTF8.GetBytes(searchString), SocketFlags.None, multicastEndPoint);
Console.WriteLine("M-Search sent...\r\n");
byte[] receiveBuffer = new byte[64000];
var now = DateTime.Now;
while ((DateTime.Now - now).TotalMilliseconds <= timeout)
{
if (udpSocket.Available > 0)
{
int receivedBytes = udpSocket.Receive(receiveBuffer, SocketFlags.None);
if (receivedBytes > 0)
{
var response = Encoding.UTF8.GetString(receiveBuffer, 0, receivedBytes);
var headers = response.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
foreach (var header in headers)
{
if (header.StartsWith("Location:", StringComparison.OrdinalIgnoreCase))
{
var url = header.Substring(header.IndexOf(":") + 1).Trim();
var ip = new Uri(url).Host;
ips.Add(ip);
}
}
}
}
}
}
ips.Sort();
return ips.ToArray();
}
"TheEndless" wrote:
Note that I get the local computer's IP address from DNS, as I have multiple network adapters (wireless, wired, vmware, etc) and using IPAddress.Any wouldn't work for me. I wonder if that could be the issue with PurpleBug...
"EnTerr" wrote:"TheEndless" wrote:
Note that I get the local computer's IP address from DNS, as I have multiple network adapters (wireless, wired, vmware, etc) and using IPAddress.Any wouldn't work for me. I wonder if that could be the issue with PurpleBug...
That's probably it! Your PC is multi-homed (on multiple networks) so it's unclear on which the discovery should be sent. It's somewhat of a miracle that given code works for you - what's the reason to expect that `.first()` will return the right interface?
"EnTerr" wrote:
Looking further, there seem to be bunch of unnecessary (likely counter-productive) incantations like ReuseAddress (no need, since ephemeral port/0), AddMembership (no need, response comes as unicast), MulticastTimeToLive (no worries, you won't break Internet), MulticastLoopback (why would i want my own host to receive my cast). My view is the less customization one does to an item, the better the odds it to work now and in the future. I.e. do whatever tweaks needed to start working, then stop. (And then i take exception to my rule by always sending "MX: 1", in case it ever gets fixed)
"TheEndless" wrote:
Since it's querying DNS instead of just looking at the local network interfaces, the assumption is that DNS will only return the valid network IP, so .First() should always be valid.
I have no answers/reasons for any of those. I got the majority of the broadcast code from an example I found online: http://stackoverflow.com/questions/1279 ... -discovery
"TheEndless" wrote:"belltown" wrote:
It works fine discovering my Rokus, and I know at least one other person whose Rokus were also discovered. So far, TheEndless is the only one who's reported a problem with the discovery mechanism.
Note that I get the local computer's IP address from DNS, as I have multiple network adapters (wireless, wired, vmware, etc) and using IPAddress.Any wouldn't work for me. I wonder if that could be the issue with PurpleBug...
"belltown" wrote:
You're using the default multicast adapter to transmit the SSDP discovery requests, as am I so far, the default multicast adapter being determined by the routing table.
I'm not too concerned about people having Rokus attached to different adapters on different subnets that aren't reachable by the SSDP request sent on the chosen interface.
"EnTerr" wrote:"TheEndless" wrote:
Since it's querying DNS instead of just looking at the local network interfaces, the assumption is that DNS will only return the valid network IP, so .First() should always be valid.
I did not say .first() will be invalid. Rather, it's a crapshoot whether the first IP you get is the one where the Rokus are.
"EnTerr" wrote:I'm not too concerned about people having Rokus attached to different adapters on different subnets that aren't reachable by the SSDP request sent on the chosen interface.
Then you needn't change anything since that's TheEndless's case - being on 2+ LANs at the same time and i bet so it happens the Rokus are on the interface with unfavorable metric (the WiFi? i won't mind looking at your "route print" if you want it investigated further).
"EnTerr" wrote:"belltown" wrote:
You're using the default multicast adapter to transmit the SSDP discovery requests, as am I so far, the default multicast adapter being determined by the routing table.
No. His code (originating from the linked SO) is not using the "default multicast adapter" (poetic license? the one with best route metric in reality) of yours. Rather, it binds to the 1st local IP looked up (via DNS in this case, albeit there is another way) and sends from there.
IP_MULTICAST_IF ... Gets or sets the outgoing interface for sending IPv4 multicast traffic. This option does not change the default interface for receiving IPv4 multicast traffic.
The input value for setting this option is a 4-byte IPv4 address in network byte order. This DWORD parameter can also be an interface index in network byte order. Any IP address in the 0.x.x.x block (first octet of 0) except IPv4 address 0.0.0.0 is treated as an interface index. An interface index is a 24-bit number, and the 0.0.0.0/8 IPv4 address block is not used (this range is reserved). The interface index can be used to specify the default interface for multicast traffic for IPv4. If optval is zero , the default interface for receiving multicast is specified for sending multicast traffic.
The default interface used for IPv4 multicast is determined by the networking stack in Windows. An application can determine the default interface used for IPv4 multicast using the GetIpForwardTable function to retrieve the IPv4 routing table. The network interface with the lowest value for the routing metric for a destination IP address of 224.0.0.0 is the default interface for IPv4 multicast. The routing table can also be displayed from the command prompt with the following command: route print. The IP_MULTICAST_IF socket option can be used to set the default interface to send IPv4 multicast packets.
Each multicast transmission is sent from a single network interface, even if the host has more than one multicast-capable interface. A socket option is available to override the default for subsequent transmissions from a given socket. For example ... (IP_MULTICAST_IF example) ... An address of INADDR_ANY may be used to revert to the default interface. Note that this address might be different from the one the socket is bound to.
The IP_MULTICAST_IF or IPV6_MULTICAST_IF settings tell your socket which interface to send its multicast packets on. It's a separate, independent setting from the interface that you bound your socket to with bind(), since bind() controls which interface(s) the socket receives multicast packets from.
"TheEndless" wrote:That's exactly what i meant too, no confusion there. I say you were lucky .first() was giving the IP you needed but the result may be different. Why don't you do the simple experiment of printing all results (foreach), instead of the 1st only? That may shed some light. (if there is only 1 result returned and PurpleBug still does not discover, then i was wrong. Simple)"EnTerr" wrote:Hehe.. I think we got stuck in BrightScript land there for a second. By "valid/invalid", I didn't mean null.. I meant the one I needed.
I did not say .first() will be invalid. Rather, it's a crapshoot whether the first IP you get is the one where the Rokus are.
Looking through my network connections, my wired connection is the only one with a DNS server defined (and wireless, but that one isn't currently connected), so perhaps that's why it works.I doubt that because that DNS server won't actually be consulted. Instead the DNS resolver library sorts it out internally (it might also use the etc/hosts file and WINS). You may feel the resolver would give undue preference to the IP with DNS but that doesn't sound right either.
All I know for sure is that IPAddress.Any doesn't work on my network, and this code does. As I mentioned above, this was a quick and dirty solution for a small utility I wrote to make running r2d2_bitmaps and parsing its output easier. I only posted it to give belltown something to compare to, so he could potentially figure out why his code doesn't work for me...He cannot reproduce your unique circumstances (i.e. make P-B not discover) and just staring at the code so far did not lead to agreement. Why don't you PM me your "route print" results? Maybe i find proof - or i back-pedal - or well, maybe nothing.
... Occasionally I may enable the wireless interface, bring up a VMWare machine, or connect to a VPN via Cisco AnyConnect, but none of those have anything to do with my Rokus.Oh - Cisco AnyConnect, you say? I wouldn't put it past it to <expletive> up the network configuration, knowing what it is known for doing.
As this is intended as a tool for developers, I'd expect more complex network configurations to be more the norm than the exception (although, I would argue that mine is not more complex, as I haven't done any custom network configuration). In contrast, Eclipse's debug console is able to discover all of my Rokus, as are several different Roku remote apps I've installed, so this discovery issue, so far, is specific to PurpleBug.You are right in that developers' machines will tend to have hairier network configs (the mere fact of VM or VPN see to that). Let's not forget P-B is a gift horse though, shan't stare too long in its UPnP mouth. As of me - i am got engaged because of the mystery it not working for you, since i like solving puzzles.