UPnP Multicast: missing answers from M-SEARCH (Discovery) - c#

I created a small program to test UPnP Multicast (Visual C# 2010 Express, running on Windows 7 Professional 64 Bit). I can receive the UPnP NOTIFY Messages from UPnP Devices in my Network. But when i send the M-SEARCH Message, i get no Answers.
I have tested the same code on a iOS environment (Monotouch for iOS, running on a iPhone simulator on a Mac). There it runs fine and i get all the search responses from my UPnP devices. I can also see the M-SEARCH message from my windows program.
It looks like Windows (or a Firewall?) is hiding the search responses. Any idea?
Here is the code:
IPEndPoint LocalEndPoint = new IPEndPoint(IPAddress.Any, 1900);
IPEndPoint MulticastEndPoint = new IPEndPoint(IPAddress.Parse("239.255.255.250"), 1900);
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:ssdp:all\r\nMX:3\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];
int ReceivedBytes = 0;
while (true)
{
if (UdpSocket.Available > 0)
{
ReceivedBytes = UdpSocket.Receive(ReceiveBuffer, SocketFlags.None);
if (ReceivedBytes > 0)
{
Console.WriteLine(Encoding.UTF8.GetString(ReceiveBuffer, 0, ReceivedBytes));
}
}
}

Yeah, I solved the problem! Small mistake, big impact:
My program is sending the M-SEARCH on port 1900 which is bound to the UPnP multicast group. Because i bound the LocalEndPoint to the same port, the UPnP devices answers with unicast to port 1900. On iOS it worked, because my program was the only service bound to this port. But on the PC, i found several services bound to port 1900 (found with "netstat -p UDP -a"). So the unicast messages from the UPnP devices was absorbed by one of the other services.
The solution: I bound the LocalEndPoint to a free port (e.g. 60000), and now it works fine!
IPEndPoint LocalEndPoint = new IPEndPoint(IPAddress.Any, 60000);

On create of local endpoint use port 0 (zero) to bind a free port not using a fixed port. Another point discovered. Binding IPAddress.Any or IPAddress.Loopback get responses from Microsoft (local?) system where as binding to one of the LAN address(es) get responses from the local net. Getting first IPV4 address can be done like this:
IPAddress localNetwork = Dns.GetHostAddresses(Environment.GetEnvironmentVariable("COMPUTERNAME")).Where(ia => (ia.AddressFamily == AddressFamily.InterNetwork)).First();

For posterity: setting all these options above is unnecessary for M-SEARCH and might even be counter-productive:
UdpSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
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);
So don't do it.

Related

How to send or forward a raw TCP packet by C# Socket on a Windows Server?

I already know that it is not possible to send raw TCP packet on windows desktops witch mentioned on Microsoft Web Site, But is it possible on new Windows Servers like Windows Server 2019. I don't want to use Pcap or third-party drivers.
Something like this?
var sendSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);
sendSocket.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastTimeToLive, 8);
sendSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
var s = new IPv4Header
{
SrcIpAddress = IPAddress.Parse("127.0.0.99"),
DesIpAddress = IPAddress.Parse("127.0.0.90"),
Protocol = 6,
Length = (ushort)Marshal.SizeOf(typeof(IPv4Header))
};
data = s.ToBytes();

How to use external IP address with c# icmp socket?

I'm doing a small project that consists on listening for a Ping and just answer back with some info.
I thought of using a Socket Raw for ICMP as listener in server-side and just send a ping from client-side, this works fine when I put the 127.0.0.1 address in the IPEndPoint, but this just let me go for local tests.
I want to ping from another computer in my ethernet connection and I must change that 127.0.0.1 address in the server-side, thought of using 192.168.0.112 as it's my local ip address, but it doesn't work. Any thoughts?
Here's my code:
server
Socket servidor = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.Icmp);
IPEndPoint endpoint = new IPEndPoint(IPAddress.Parse("127.0.0.1"), 0);
servidor.Bind(endpoint);
servidor.IOControl(IOControlCode.ReceiveAll, new byte[] { 1, 0, 0, 0 }, new byte[] { 1, 0, 0, 0 });
Any help will be really appreciated! Thank you!

Empty AddressList in GetHostEntry

I am using following code to connect to remote host:
IPHostEntry hostname = Dns.GetHostEntry("172.29.65.33");
IPAddress address = hostname.AddressList[0]; // IndexOutOfRangeException
...
My question is why AddressList is empty? Host is there, no SocketException is thrown...
Some details: customer has upgraded Windows XP to 8.1 and then all troubles begun.
I've read this and this topic, but unfortunately they are not useful to fix an issue, therefore asking it again.
Instead of resolving addresses via Dns
IPHostEntry hostname = Dns.GetHostEntry("172.29.65.33");
IPAddress address = hostname.AddressList[0];
...
IPEndPoint end = new IPEndPoint(address, port);
it can be simply parsed
IPEndPoint end = new IPEndPoint(IPAddress.Parse("172.29.65.33"), port);
This will eliminate all problems (socket exception, empty address list, etc.) related to using Dns.

c# socket sends // binds udp multicasts not to the selected network interface

Hello i got 2 Networkadapters on my PC and want to send udp multicasts to group 239.0.0.222 Port 9050 on the selected Network interface.
But it only works with the first interface, when choosing another NIC no data is sent.
The localIP is the local Ip from the selected adapter
The senders code:
IPAddress localIP = getLocalIpAddress();
IPAddress multicastaddress = IPAddress.Parse("239.0.0.222");
IPEndPoint remoteep = new IPEndPoint(multicastaddress, 9050);
UdpClient udpclient = new UdpClient(9050);
MulticastOption mcastOpt = new MulticastOption(multicastaddress,localIP);
udpclient.Client.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.AddMembership, mcastOpt);
udpclient.Send(data, data.Length, remoteep);
EDIT1:
Code for adapters local IP:
NetworkInterface.GetAllNetworkInterfaces()[adapterIndex].GetIPProperties().UnicastAddresses[0].Address;
EDIT2,5:
Also tried both of with same reuslt
Wireshark displays me the correct join of the multicast group on the second adapter
udpclient.JoinMulticastGroup(multicastaddress);
udpclient.Client.Bind(remoteep);
EDIT3:
I now tried on another PC but the same problem happens again, Adapter1 runs, on all others nothing is sent.
Another thing i tried out, is to switch the order of the first two adapters in the windows xp config, then again the new first adapter works but the new second sends nothing.
By default, only first adapter joins to given multicast group. From OS perspective, it's absolutely relevant because the group would provide the same content whatever adapter consume the multicast stream. If you plan to listen the multicast on each of your adapters, you have to iterate over them and place appropriate socket option on each:
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface adapter in nics)
{
IPInterfaceProperties ip_properties = adapter.GetIPProperties();
if (!adapter.GetIPProperties().MulticastAddresses.Any())
continue; // most of VPN adapters will be skipped
if (!adapter.SupportsMulticast)
continue; // multicast is meaningless for this type of connection
if (OperationalStatus.Up != adapter.OperationalStatus)
continue; // this adapter is off or not connected
IPv4InterfaceProperties p = adapter.GetIPProperties().GetIPv4Properties();
if (null == p)
continue; // IPv4 is not configured on this adapter
my_sock.SetSocketOption(SocketOptionLevel.IP, SocketOptionName.MulticastInterface, (int)IPAddress.HostToNetworkOrder(p.Index));
}
P.S. Yep, I'm "this guy" mentioned by #lukebuehler: http://windowsasusual.blogspot.ru/2013/01/socket-option-multicast-interface.html
I think this guy has the answer, you have to iterate over the network interfaces and find the one that supports multicast.
http://windowsasusual.blogspot.ru/2013/01/socket-option-multicast-interface.html

What is the best way for a client app to find a server on a local network in C#?

The client connects to the server using GenuineChannels (we are considering switching to DotNetRemoting). What I mean by find is obtain the IP and port number of a server to connect to.
It seems like a brute-force approach would be try every IP on the network try the active ports (not even sure if that's possible) but there must be a better way.
Consider broadcasting a specific UDP packet. When the server or servers see the broadcasted UDP packet they send a reply. The client can collect the replies from all the servers and start connecting to them or based on an election algorithm.
See example for client (untested code):
using System.Net;
using System.Net.Sockets;
[STAThread]
static void Main(string[] args)
{
Socket socket = new Socket(AddressFamily.InterNetwork,
SocketType.Dgram, ProtocolType.Udp);
socket.Bind(new IPEndPoint(IPAddress.Any, 8002));
socket.Connect(new IPEndPoint(IPAddress.Broadcast, 8001));
socket.Send(System.Text.ASCIIEncoding.ASCII.GetBytes("hello"));
int availableBytes = socket.Available;
if (availableBytes > 0)
{
byte[] buffer = new byte[availableBytes];
socket.Receive(buffer, 0, availableBytes, SocketFlags.None);
// buffer has the information on how to connect to the server
}
}
I'd say the best way is to use Bonjour/Zeroconf/mDNS for C#; a lot of thought went into making it play nice with the network; IE it pings less frequently over time if possible, etc. There's Mono.Zeroconf, and I read there's an older .NET project in the Apple SDK but I haven't found it.
So the easiest would be to install Bonjour for Windows, then get the Windows Binaries for Mono.Zeroconf try the example MZClient.exe drop the Mono.Zeroconf.dll and/or Mono.Zeroconf.Providers.Bonjour.dll into your project references and go.
Something like this:
var service = new Mono.Zeroconf.RegisterService {
Name = "Use Me for Stuff",
RegType = "_daap._tcp",
ReplyDomain = "local.",
Port = 0024200,
TxtRecord = new Mono.Zeroconf.TxtRecord {
{"I have no idea what's going on", "true"}}
};
service.Register();
var browser = new Mono.Zeroconf.ServiceBrowser();
browser.ServiceAdded +=
delegate(object o, Mono.Zeroconf.ServiceBrowseEventArgs args) {
Console.WriteLine("Found Service: {0}", args.Service.Name);
args.Service.Resolved +=
delegate(object o, Mono.Zeroconf.ServiceBrowseEventArgs args) {
var s = args.Service;
Console.WriteLine(
"Resolved Service: {0} - {1}:{2} ({3} TXT record entries)",
s.FullName, s.HostEntry.AddressList[0], s.Port, s.TxtRecord.Count);
};
args.Service.Resolve();
};
browser.Browse("_daap._tcp", "local");
Just wanted to point out an alternative Zeroconf NuGet package: Zeroconf. It does not have any native dependencies, so you don't need to install Bonjour for Windows or anything else.
It has support for .NET 4.5, WP8 and Win8.
WS-Discovery is a protocol intended for this purpose. It has a few different variations, different flavors of broadcasting and proxies. http://en.wikipedia.org/wiki/WS-Discovery
.NET WCF4 implements this.
Have the server listen for broadcast on a specific port on the network (must use UDP), When client starts have it broadcast some "ping" request on that port. when the server sees a "ping" it send back a message with the TCP address and port required for the client to connect to it.

Categories