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 ;
}
public ISocket 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 ;
}
return new NetSocket ( socket , _logger ) ;
2016-11-08 18:44:23 +00:00
}
2016-12-05 18:46:38 +00:00
catch ( SocketException ex )
{
if ( dualMode )
{
_logger . Error ( "Error creating dual mode socket: {0}. Will retry with ipv4-only." , ex . SocketErrorCode ) ;
if ( ex . SocketErrorCode = = SocketError . AddressFamilyNotSupported )
{
return CreateSocket ( IpAddressFamily . InterNetwork , socketType , protocolType , false ) ;
}
}
2016-11-08 18:44:23 +00:00
2016-12-05 18:46:38 +00:00
throw ;
}
2016-11-04 08:31:05 +00:00
}
#region ISocketFactory Members
2016-11-04 18:56:47 +00:00
/// <summary>
/// Creates a new UDP socket and binds it to the specified local port.
/// </summary>
/// <param name="localPort">An integer specifying the local port to bind the socket to.</param>
public IUdpSocket CreateUdpSocket ( int localPort )
{
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 ;
}
}
2016-11-04 08:31:05 +00:00
/// <summary>
/// Creates a new UDP socket 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="IUdpSocket"/> interface used by RSSDP components to perform socket operations.</returns>
2016-12-04 21:30:38 +00:00
public IUdpSocket 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>
/// Creates a new UDP socket that is a member of the specified multicast IP address, and binds it to the specified local port.
/// </summary>
/// <param name="ipAddress">The multicast IP address to make the socket a member of.</param>
/// <param name="multicastTimeToLive">The multicast time to live value for the socket.</param>
/// <param name="localPort">The number of the local port to bind to.</param>
/// <returns></returns>
public IUdpSocket CreateUdpMulticastSocket ( string ipAddress , int multicastTimeToLive , int localPort )
{
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
{
#if NETSTANDARD1_3
// The ExclusiveAddressUse socket option is a Windows-specific option that, when set to "true," tells Windows not to allow another socket to use the same local address as this socket
// See https://github.com/dotnet/corefx/pull/11509 for more details
if ( System . Runtime . InteropServices . RuntimeInformation . IsOSPlatform ( System . Runtime . InteropServices . OSPlatform . Windows ) )
{
retVal . ExclusiveAddressUse = false ;
}
#else
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 ;
}
}
#endregion
}
}