2016-11-04 08:31:05 +00:00
using System ;
using System.Collections.Generic ;
using System.Linq ;
using System.Net ;
using System.Net.Sockets ;
using System.Threading.Tasks ;
2016-12-04 21:30:38 +00:00
using Emby.Common.Implementations.Networking ;
2016-11-08 18:44:23 +00:00
using MediaBrowser.Model.Logging ;
2016-11-04 08:31:05 +00:00
using MediaBrowser.Model.Net ;
namespace Emby.Common.Implementations.Net
{
public class SocketFactory : ISocketFactory
{
// THIS IS A LINKED FILE - SHARED AMONGST MULTIPLE PLATFORMS
// Be careful to check any changes compile and work for all platform projects it is shared in.
// Not entirely happy with this. Would have liked to have done something more generic/reusable,
// but that wasn't really the point so kept to YAGNI principal for now, even if the
// interfaces are a bit ugly, specific and make assumptions.
2016-11-13 21:04:21 +00:00
private readonly ILogger _logger ;
2016-11-08 18:44:23 +00:00
public SocketFactory ( ILogger logger )
{
2016-11-13 21:04:21 +00:00
if ( logger = = null )
{
throw new ArgumentNullException ( "logger" ) ;
}
2016-11-08 18:44:23 +00:00
_logger = logger ;
}
2017-03-03 05:53:21 +00:00
public IAcceptSocket CreateSocket ( IpAddressFamily family , MediaBrowser . Model . Net . SocketType socketType , MediaBrowser . Model . Net . ProtocolType protocolType , bool dualMode )
2016-11-04 08:31:05 +00:00
{
2016-12-05 18:46:38 +00:00
try
{
var addressFamily = family = = IpAddressFamily . InterNetwork
? AddressFamily . InterNetwork
: AddressFamily . InterNetworkV6 ;
2016-11-08 18:44:23 +00:00
2016-12-05 18:46:38 +00:00
var socket = new Socket ( addressFamily , System . Net . Sockets . SocketType . Stream , System . Net . Sockets . ProtocolType . Tcp ) ;
2016-11-08 18:44:23 +00:00
2016-12-05 18:46:38 +00:00
if ( dualMode )
{
socket . DualMode = true ;
}
2017-03-02 20:50:09 +00:00
return new NetAcceptSocket ( socket , _logger , dualMode ) ;
2016-11-08 18:44:23 +00:00
}
2016-12-05 18:46:38 +00:00
catch ( SocketException ex )
{
2016-12-07 20:02:34 +00:00
throw new SocketCreateException ( ex . SocketErrorCode . ToString ( ) , ex ) ;
2016-12-05 18:46:38 +00:00
}
2017-03-17 20:23:34 +00:00
catch ( ArgumentException ex )
{
if ( dualMode )
{
// Mono for BSD incorrectly throws ArgumentException instead of SocketException
throw new SocketCreateException ( "AddressFamilyNotSupported" , ex ) ;
}
else
{
throw ;
}
}
2016-11-04 08:31:05 +00:00
}
2017-03-02 20:50:09 +00:00
public ISocket CreateTcpSocket ( IpAddressInfo remoteAddress , int remotePort )
{
if ( remotePort < 0 ) throw new ArgumentException ( "remotePort cannot be less than zero." , "remotePort" ) ;
2017-03-29 06:53:26 +00:00
var addressFamily = remoteAddress . AddressFamily = = IpAddressFamily . InterNetwork
? AddressFamily . InterNetwork
: AddressFamily . InterNetworkV6 ;
var retVal = new Socket ( addressFamily , System . Net . Sockets . SocketType . Stream , System . Net . Sockets . ProtocolType . Tcp ) ;
2017-03-02 20:50:09 +00:00
try
{
retVal . SetSocketOption ( SocketOptionLevel . Socket , SocketOptionName . ReuseAddress , true ) ;
2017-03-29 06:53:26 +00:00
}
catch ( SocketException )
{
// This is not supported on all operating systems (qnap)
}
try
{
2017-03-02 20:50:09 +00:00
return new UdpSocket ( retVal , new IpEndPointInfo ( remoteAddress , remotePort ) ) ;
}
catch
{
if ( retVal ! = null )
retVal . Dispose ( ) ;
throw ;
}
}
2016-11-04 08:31:05 +00:00
2016-11-04 18:56:47 +00:00
/// <summary>
2017-03-02 20:50:09 +00:00
/// Creates a new UDP acceptSocket and binds it to the specified local port.
2016-11-04 18:56:47 +00:00
/// </summary>
2017-03-02 20:50:09 +00:00
/// <param name="localPort">An integer specifying the local port to bind the acceptSocket to.</param>
public ISocket CreateUdpSocket ( int localPort )
2016-11-04 18:56:47 +00:00
{
if ( localPort < 0 ) throw new ArgumentException ( "localPort cannot be less than zero." , "localPort" ) ;
2016-11-08 18:44:23 +00:00
var retVal = new Socket ( AddressFamily . InterNetwork , System . Net . Sockets . SocketType . Dgram , System . Net . Sockets . ProtocolType . Udp ) ;
2016-11-04 18:56:47 +00:00
try
{
retVal . SetSocketOption ( SocketOptionLevel . Socket , SocketOptionName . ReuseAddress , true ) ;
2016-12-04 21:30:38 +00:00
return new UdpSocket ( retVal , localPort , IPAddress . Any ) ;
2016-11-04 18:56:47 +00:00
}
catch
{
if ( retVal ! = null )
retVal . Dispose ( ) ;
throw ;
}
}
2017-03-13 04:08:23 +00:00
public ISocket CreateUdpBroadcastSocket ( int localPort )
{
if ( localPort < 0 ) throw new ArgumentException ( "localPort cannot be less than zero." , "localPort" ) ;
var retVal = new Socket ( AddressFamily . InterNetwork , System . Net . Sockets . SocketType . Dgram , System . Net . Sockets . ProtocolType . Udp ) ;
try
{
retVal . SetSocketOption ( SocketOptionLevel . Socket , SocketOptionName . ReuseAddress , true ) ;
retVal . SetSocketOption ( SocketOptionLevel . Socket , SocketOptionName . Broadcast , 1 ) ;
return new UdpSocket ( retVal , localPort , IPAddress . Any ) ;
}
catch
{
if ( retVal ! = null )
retVal . Dispose ( ) ;
throw ;
}
}
2016-11-04 08:31:05 +00:00
/// <summary>
2017-03-13 04:08:23 +00:00
/// Creates a new UDP acceptSocket that is a member of the SSDP multicast local admin group and binds it to the specified local port.
/// </summary>
/// <returns>An implementation of the <see cref="ISocket"/> interface used by RSSDP components to perform acceptSocket operations.</returns>
2017-03-02 20:50:09 +00:00
public ISocket CreateSsdpUdpSocket ( IpAddressInfo localIpAddress , int localPort )
2016-11-04 08:31:05 +00:00
{
if ( localPort < 0 ) throw new ArgumentException ( "localPort cannot be less than zero." , "localPort" ) ;
2016-11-08 18:44:23 +00:00
var retVal = new Socket ( AddressFamily . InterNetwork , System . Net . Sockets . SocketType . Dgram , System . Net . Sockets . ProtocolType . Udp ) ;
2016-11-04 08:31:05 +00:00
try
{
retVal . SetSocketOption ( SocketOptionLevel . Socket , SocketOptionName . ReuseAddress , true ) ;
retVal . SetSocketOption ( SocketOptionLevel . IP , SocketOptionName . MulticastTimeToLive , 4 ) ;
2016-12-04 21:30:38 +00:00
var localIp = NetworkManager . ToIPAddress ( localIpAddress ) ;
retVal . SetSocketOption ( SocketOptionLevel . IP , SocketOptionName . AddMembership , new MulticastOption ( IPAddress . Parse ( "239.255.255.250" ) , localIp ) ) ;
return new UdpSocket ( retVal , localPort , localIp ) ;
2016-11-04 08:31:05 +00:00
}
catch
{
if ( retVal ! = null )
retVal . Dispose ( ) ;
throw ;
}
}
/// <summary>
2017-03-02 20:50:09 +00:00
/// Creates a new UDP acceptSocket that is a member of the specified multicast IP address, and binds it to the specified local port.
2016-11-04 08:31:05 +00:00
/// </summary>
2017-03-02 20:50:09 +00:00
/// <param name="ipAddress">The multicast IP address to make the acceptSocket a member of.</param>
/// <param name="multicastTimeToLive">The multicast time to live value for the acceptSocket.</param>
2016-11-04 08:31:05 +00:00
/// <param name="localPort">The number of the local port to bind to.</param>
/// <returns></returns>
2017-03-02 20:50:09 +00:00
public ISocket CreateUdpMulticastSocket ( string ipAddress , int multicastTimeToLive , int localPort )
2016-11-04 08:31:05 +00:00
{
if ( ipAddress = = null ) throw new ArgumentNullException ( "ipAddress" ) ;
if ( ipAddress . Length = = 0 ) throw new ArgumentException ( "ipAddress cannot be an empty string." , "ipAddress" ) ;
if ( multicastTimeToLive < = 0 ) throw new ArgumentException ( "multicastTimeToLive cannot be zero or less." , "multicastTimeToLive" ) ;
if ( localPort < 0 ) throw new ArgumentException ( "localPort cannot be less than zero." , "localPort" ) ;
2016-11-08 18:44:23 +00:00
var retVal = new Socket ( AddressFamily . InterNetwork , System . Net . Sockets . SocketType . Dgram , System . Net . Sockets . ProtocolType . Udp ) ;
2016-11-04 08:31:05 +00:00
try
{
2016-12-13 23:38:26 +00:00
#if NET46
retVal . ExclusiveAddressUse = false ;
#else
2017-03-02 20:50:09 +00:00
// The ExclusiveAddressUse acceptSocket option is a Windows-specific option that, when set to "true," tells Windows not to allow another acceptSocket to use the same local address as this acceptSocket
2016-12-13 23:38:26 +00:00
// See https://github.com/dotnet/corefx/pull/11509 for more details
if ( System . Runtime . InteropServices . RuntimeInformation . IsOSPlatform ( System . Runtime . InteropServices . OSPlatform . Windows ) )
2016-11-04 08:31:05 +00:00
{
retVal . ExclusiveAddressUse = false ;
}
#endif
2016-11-14 19:48:01 +00:00
//retVal.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.Broadcast, true);
2016-11-04 08:31:05 +00:00
retVal . SetSocketOption ( SocketOptionLevel . Socket , SocketOptionName . ReuseAddress , true ) ;
retVal . SetSocketOption ( SocketOptionLevel . IP , SocketOptionName . MulticastTimeToLive , multicastTimeToLive ) ;
2016-12-04 21:30:38 +00:00
var localIp = IPAddress . Any ;
retVal . SetSocketOption ( SocketOptionLevel . IP , SocketOptionName . AddMembership , new MulticastOption ( IPAddress . Parse ( ipAddress ) , localIp ) ) ;
2016-11-04 08:31:05 +00:00
retVal . MulticastLoopback = true ;
2016-12-04 21:30:38 +00:00
return new UdpSocket ( retVal , localPort , localIp ) ;
2016-11-04 08:31:05 +00:00
}
catch
{
if ( retVal ! = null )
retVal . Dispose ( ) ;
throw ;
}
}
}
}