alanmcgovern / Mono.Nat

UPNP and NAT-PMP port forwarding for .NET
https://github.com/mono/Mono.Nat
MIT License
163 stars 156 forks source link

Implemented DeviceUnknown event #24

Closed BaronGreenback closed 4 years ago

BaronGreenback commented 4 years ago

Helps solves an issue whereby two processes listening on the same interface/port interact randomly.

e., Jellyfin implements a DLNA server on the same interfaces/ports so was randomly picking up Mono.NAT's broadcast replies and loosing its own.

By implementing a device unknown - it has been possible to turn the sockets with gateways into send only, thus ensuring Mono works as designed.

By implementing this event for any comms that arrive on mono's sockets, it's possible to feed them back through into Jellyfin's processing routines and for the two to exist in harmony.

BaronGreenback commented 4 years ago

Apologies but it looks like my VS did some auto removal of spaces.

BaronGreenback commented 4 years ago

Added a mod to make it work alongside M$ SSDPSRV which steals the responses.

alanmcgovern commented 4 years ago

SSDPSRV

Added a mod to make it work alongside M$ SSDPSRV which steals the responses.

What exactly does SSDPSRV break? That's a fairly standard windows service and it doesn't seem to interfere with mono.nat for me.

BaronGreenback commented 4 years ago

Happy to pull in the changes and test - same with the header licence. Am looking to use the mono.nat source direct - as it will be so much easier to get my JellyFin network PR accepted.

It's been a very long day so apologies if it get things mixed up.

The issue with the M$ service is that mono isn't receiving any of the Notifiy packets whilst the service is running. (Win 7 64 bit) It does receive some search messages but they don't have a location - so must be from the device.

As i understand the current code - mono listens on a random port for search responses - so doesn't pick up the ssdp replies. I tried assigning port 1900, but m$ got there first.

Adding in the 0.0.0.0/1900 multicast solves the issue - (it's the same method that Jellyfin implemented). Mono transmits on 1900, and then sees some responses on 0.0.0.0.

The ideal solution would be to separate the sockets comms and the processing - allowing external message processing to bypass the udp functionality. That's how i've linked in JellyFin.

When mono is active, JF gives way to mono.nat the gateway port listening task, relying on the unknown event for the processing.

If you're looking for mono to be the one stop shop for uPnP - it might be worth providing public access to the internal string parsing - thus bypassing any socket clashing. That way if someone has implemented some sort of DLNA/SSDP there won't be a clash.

Hope this makes sense- and thanks again for all you're doing.

alanmcgovern commented 4 years ago

The ideal solution would be to separate the sockets comms and the processing - allowing external message processing to bypass the udp functionality. That's how i've linked in JellyFin.

The thought crossed my mind as soon as you pushed the change to allow IUnknown :) Your proposed changes solve the problem only as long as Mono.Nat is the thing in charge, but if something else is in charge then the messages will need to be passed into Mono.Nat.

As i understand the current code - mono listens on a random port for search responses - so doesn't pick up the ssdp replies. I tried assigning port 1900, but m$ got there first.

This description is partially correct. The spec describes this in better detail, but the short of it is that the expectation is that programs will bind to a random UDP port. Once they subscribe to the relevant multicast group they can send the SEARCH broadcast message to it. Any device which receives that broadcast message will respond to the IPAddress/Port where the broadcast message originated from. This is how mono.nat expects to receive the replies, and it works in practice or no devices would ever be discovered :) Essentially the search is multi-cast, but the response is uni-cast directly to mono.nat.

The issue with the M$ service is that mono isn't receiving any of the Notify packets whilst the service is running. (Win 7 64 bit) It does receive some search messages but they don't have a location - so must be from the device.

I implemented mono.nat a long time ago and haven't re-read the spec much since then. I don't know enough about Notify packets to know why they aren't being picked up with the code as-is today. I suspect it's because the UdpClients used for receiving messages aren't joined to the multicast group. Perhaps if we made that change, rather than creating a brand new UpdClient which does this, it would work as expected? I can test that out in a bit myself!

Take your time with any changes, I just didn't want to go trampling over your work with 'refactoring' and 'improving' without involving you :) Thanks for the contributions and suggestions!

alanmcgovern commented 4 years ago

I added a tweak to better handle Notify messages. If i attach a logger it looks like they are showing up as expected.

Patch: https://github.com/alanmcgovern/Mono.Nat/commit/f3cbf481211319061e2cbd197805501ebb50a1c2?w=1

Logger: Logger.Factory = prefix => new TextLogger(Console.Out, prefix);

Example notify packet received my the library:

INFO: Mono.Nat.Upnp.UpnpSearcher:uPnP Search Response: NOTIFY * HTTP/1.1
Host: 239.255.255.250:1900
NT: urn:dial-multiscreen-org:device:dial:1
NTS: ssdp:alive
Location: http://192.168.9.178:2869/upnphost/udhisapi.dll?content=uuid:82a02b6c-51fe-4404-b87c-03d17f9613fd
USN: uuid:82a02b6c-51fe-4404-b87c-03d17f9613fd::urn:dial-multiscreen-org:device:dial:1
Cache-Control: max-age=1800
Server: Microsoft-Windows/10.0 UPnP/1.0 UPnP-Device-Host/1.0
OPT:"http://schemas.upnp.org/upnp/1/0/"; ns=01
01-NLS: ef7f013b09f97cd7baf648ceb894eb15
BaronGreenback commented 4 years ago

Have imported your version and it looks to be working :-)

Have altered the PR version i submitted and created a

NatUtility.ParseMessage(NatProtocol type, IPAddress localAddress, byte[] content, IPEndPoint source)

It would provide the external processing mentioned above.

Haven't linked into it - so feel free to ignore.

alanmcgovern commented 4 years ago

Brilliant, thanks for this!

alanmcgovern commented 4 years ago

The changes are in master now! Thanks for the work, this should (hopefully) make things a little easier for integration.

I'm going to do a dreaded reformat of the code and add a .editorconfig. The mix of tabs and spaces is quite annoying to work with :)