5

Good morning,

We've decided to use UPnP as much as possible. We are using MultiCast on 239.255.255.250:1900 for our M-SEARCH.

However, we're looking at how to handle when a customer has MultiCast locked down on their network. Looking at the UPnP 1.1 spec, it talks about using a unicast with M-SEARCH. So, if we already know the IP addresses of the various devices we want to talk to, and they are listening on 0.0.0.0:1900, we're thinking we could send a unicast M-SEARCH to each device on deviceIP:1900.

I've been trying to do this and am having one heck of a time getting the devices to receive and respond to the unicast M-SEARCH request.

First, is it allowed have your first UPnP conversation with a device start with a unicast M-SEARCH?

Second, is there some reason that listening on 0.0.0.0:1900 wouldn't accept a message sent to the deviceIP:1900?

When I do a netstat on my machine to see what IPs and Ports are in use, it appears that either 239.255.255.250:1900 is not on the list, or that it is appearing as 0.0.0.0:1900.

So, if 0.0.0.0 is (ANY_IP) then would having a single listener listening on 0.0.0.0:1900 be sufficient to receive any messages MultiCast to 239.255.255.250:1900 and any sent directly via unicast to that machine's ip:1900?

When testing, I'm able to always receive MultiCasts, but I never receive unicasts for M-SEARCH. I am able to communicate with devices on their other ports when doing a GET and such, but just seems like I can't get port 1900 to respond to unicast M-SEARCH.

Can you actually listen on 239.255.255.250:1900 as multicast and listen on 0.0.0.0:1900 as unicast at the same time on the same machine without a udp socket conflict?

Any advice and pointers on this would be greatly appreciated.

Thanks, Curtis

PS: The code I'm using is below. For address in the constructor, we're passing in IPAddress.Any (which is 0.0.0.0) and Protocol.Port is 1900. This is running on a windows machine under Windows 8.1:

//
// SsdpSocket.cs
//
// Author:
//   Aaron Bockover <abockover@novell.com>
//
// Copyright (C) 2008 Novell, Inc.
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//

using System;
using System.Net;
using System.Net.Sockets;

namespace Mono.Ssdp.Mono.Ssdp.Internal
{
    class SsdpSocket : Socket
    {
        static readonly IPEndPoint ssdp_send_point = new IPEndPoint (Protocol.IPAddress, Protocol.Port);

        readonly IPEndPoint ssdp_receive_point;

        public SsdpSocket (IPAddress address)
            : base (AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
        {
            ssdp_receive_point = new IPEndPoint (address, Protocol.Port);
            SetSocketOption (SocketOptionLevel.Socket, SocketOptionName.ReuseAddress, true);
        }

        public IAsyncResult BeginSendTo (byte [] data, AsyncCallback callback)
        {
            return BeginSendTo (data, callback, ssdp_send_point);
        }

        public IAsyncResult BeginSendTo (byte[] data, AsyncCallback callback, IPEndPoint endPoint)
        {
            return BeginSendTo (data, 0, data.Length, SocketFlags.None, endPoint, callback, this);
        }

        public IAsyncResult BeginReceiveFrom (AsyncReceiveBuffer buffer, AsyncCallback callback)
        {
            return base.BeginReceiveFrom (buffer.Buffer, 0, buffer.Buffer.Length, SocketFlags.None, 
                ref buffer.SenderEndPoint, callback, buffer);
        }

        public void Bind ()
        {
            Bind (ssdp_receive_point);
        }
    }
}
Curtis
  • 5,794
  • 8
  • 50
  • 77
  • 1
    Can you show the code that binds the receive socket and mention the OS it happens on? – Jussi Kukkonen Mar 05 '15 at 12:14
  • I've added source code to my posting above. – Curtis Mar 05 '15 at 20:17
  • 1
    Code looks fine to me (although I'm no C# expert). I vaguely remember people complaining that the ssdp service that runs on Windows by default affected receiving somehow, and that there was a workaround... Sorry I don't have anything more concrete but maybe that helps with googling further. – Jussi Kukkonen Mar 05 '15 at 20:24
  • I turned off the ssdp service. Still seems like somethings blocking 0.0.0.0:1900, but I don't know what.. I also tried using port 1910 in case 1900 had a conflict, but that didn't work either.. Maybe the code is discarding the unicast messages for some reason... – Curtis Mar 05 '15 at 20:27
  • 1
    Do the devices you're searching for implement UPnP v1.1? Most devices I've come across are v1.0 so would be unaware of any 1.1 features. – simonc Mar 06 '15 at 09:19
  • I'm implementing the devices and the client. – Curtis Mar 06 '15 at 13:46

1 Answers1

2

The fix for this was in the validation of the Unicast message.

Here are two examples of messages. The first is a MultiCast, the second is a UniCast:

M-SEARCH * HTTP/1.1
HOST: 239.255.255.250:1900 
MAN: "ssdp:discover" 
MX: seconds to delay response 
ST: search target 
USER-AGENT: OS/version UPnP/1.1 product/version



M-SEARCH * HTTP/1.1
HOST: hostname:portNumber
MAN: "ssdp:discover"
ST: search target
USER-AGENT: OS/version UPnP/1.1 product/version

Please note, the second M-SEARCH is a unicast search and is NOT required to have the 'MX:' line in it. The code I was using was requiring the MX: line and using its value. If there was no MX: line, we were getting an exception that was quietly hidden away and gobbled up by a catch(exception){}

Anyway, the UPnPServer just listens on 0.0.0.0:1900 and will hear all msearches that are Multi-Cast and that are Unicast.

Curtis
  • 5,794
  • 8
  • 50
  • 77
  • I'm experiencing a similar problem using the ASIO C++ library. In my case, I got unicast messages as long as listening on ports other than 1900. Using 1900, I cannot get any unicast messages. I think another application has opened port 1900 in non-reuse mode. – Robert May 21 '22 at 06:06
  • The first thing I would try would be to make sure your firewall is not blocking UPNP ports. I've noticed IT department sometimes block that port or block all ports unless you specify that they open one. So if you turn off your firewall and test that would indicate if it's a firewall issue. The other possibility that you mentioned above that another application may be using the port shouldn't matter because your broadcasting. – Curtis May 21 '22 at 18:20
  • It's not the broadcasting that is the problem. Multicasts work on port 1900. It's the unicasts to port 1900 that don't work. But if I choose any other port number, unicasts work too. Very strange. – Robert May 22 '22 at 06:26