using System; using System.Collections.Generic; using System.Net; using System.Net.Sockets; using System.Runtime.CompilerServices; using System.Runtime.InteropServices; namespace LiteNetLib { internal readonly struct NativeAddr : IEquatable { //common parts private readonly long _part1; //family, port, etc private readonly long _part2; //ipv6 parts private readonly long _part3; private readonly int _part4; private readonly int _hash; public NativeAddr(byte[] address, int len) { _part1 = BitConverter.ToInt64(address, 0); _part2 = BitConverter.ToInt64(address, 8); if (len > 16) { _part3 = BitConverter.ToInt64(address, 16); _part4 = BitConverter.ToInt32(address, 24); } else { _part3 = 0; _part4 = 0; } _hash = (int)(_part1 >> 32) ^ (int)_part1 ^ (int)(_part2 >> 32) ^ (int)_part2 ^ (int)(_part3 >> 32) ^ (int)_part3 ^ _part4; } public override int GetHashCode() { return _hash; } public bool Equals(NativeAddr other) { return _part1 == other._part1 && _part2 == other._part2 && _part3 == other._part3 && _part4 == other._part4; } public override bool Equals(object obj) { return obj is NativeAddr other && Equals(other); } public static bool operator ==(NativeAddr left, NativeAddr right) { return left.Equals(right); } public static bool operator !=(NativeAddr left, NativeAddr right) { return !left.Equals(right); } } internal class NativeEndPoint : IPEndPoint { public readonly byte[] NativeAddress; public NativeEndPoint(byte[] address) : base(IPAddress.Any, 0) { NativeAddress = new byte[address.Length]; Buffer.BlockCopy(address, 0, NativeAddress, 0, address.Length); short family = (short)((address[1] << 8) | address[0]); Port =(ushort)((address[2] << 8) | address[3]); if ((NativeSocket.UnixMode && family == NativeSocket.AF_INET6) || (!NativeSocket.UnixMode && (AddressFamily)family == AddressFamily.InterNetworkV6)) { uint scope = unchecked((uint)( (address[27] << 24) + (address[26] << 16) + (address[25] << 8) + (address[24]))); #if NETCOREAPP || NETSTANDARD2_1 || NETSTANDARD2_1_OR_GREATER Address = new IPAddress(new ReadOnlySpan(address, 8, 16), scope); #else byte[] addrBuffer = new byte[16]; Buffer.BlockCopy(address, 8, addrBuffer, 0, 16); Address = new IPAddress(addrBuffer, scope); #endif } else //IPv4 { long ipv4Addr = unchecked((uint)((address[4] & 0x000000FF) | (address[5] << 8 & 0x0000FF00) | (address[6] << 16 & 0x00FF0000) | (address[7] << 24))); Address = new IPAddress(ipv4Addr); } } } internal static class NativeSocket { static #if LITENETLIB_UNSAFE unsafe #endif class WinSock { private const string LibName = "ws2_32.dll"; [DllImport(LibName, SetLastError = true)] public static extern int recvfrom( IntPtr socketHandle, [In, Out] byte[] pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [Out] byte[] socketAddress, [In, Out] ref int socketAddressSize); [DllImport(LibName, SetLastError = true)] internal static extern int sendto( IntPtr socketHandle, #if LITENETLIB_UNSAFE byte* pinnedBuffer, #else [In] byte[] pinnedBuffer, #endif [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize); } static #if LITENETLIB_UNSAFE unsafe #endif class UnixSock { private const string LibName = "libc"; [DllImport(LibName, SetLastError = true)] public static extern int recvfrom( IntPtr socketHandle, [In, Out] byte[] pinnedBuffer, [In] int len, [In] SocketFlags socketFlags, [Out] byte[] socketAddress, [In, Out] ref int socketAddressSize); [DllImport(LibName, SetLastError = true)] internal static extern int sendto( IntPtr socketHandle, #if LITENETLIB_UNSAFE byte* pinnedBuffer, #else [In] byte[] pinnedBuffer, #endif [In] int len, [In] SocketFlags socketFlags, [In] byte[] socketAddress, [In] int socketAddressSize); } public static readonly bool IsSupported = false; public static readonly bool UnixMode = false; public const int IPv4AddrSize = 16; public const int IPv6AddrSize = 28; public const int AF_INET = 2; public const int AF_INET6 = 10; private static readonly Dictionary NativeErrorToSocketError = new Dictionary { { 13, SocketError.AccessDenied }, //EACCES { 98, SocketError.AddressAlreadyInUse }, //EADDRINUSE { 99, SocketError.AddressNotAvailable }, //EADDRNOTAVAIL { 97, SocketError.AddressFamilyNotSupported }, //EAFNOSUPPORT { 11, SocketError.WouldBlock }, //EAGAIN { 114, SocketError.AlreadyInProgress }, //EALREADY { 9, SocketError.OperationAborted }, //EBADF { 125, SocketError.OperationAborted }, //ECANCELED { 103, SocketError.ConnectionAborted }, //ECONNABORTED { 111, SocketError.ConnectionRefused }, //ECONNREFUSED { 104, SocketError.ConnectionReset }, //ECONNRESET { 89, SocketError.DestinationAddressRequired }, //EDESTADDRREQ { 14, SocketError.Fault }, //EFAULT { 112, SocketError.HostDown }, //EHOSTDOWN { 6, SocketError.HostNotFound }, //ENXIO { 113, SocketError.HostUnreachable }, //EHOSTUNREACH { 115, SocketError.InProgress }, //EINPROGRESS { 4, SocketError.Interrupted }, //EINTR { 22, SocketError.InvalidArgument }, //EINVAL { 106, SocketError.IsConnected }, //EISCONN { 24, SocketError.TooManyOpenSockets }, //EMFILE { 90, SocketError.MessageSize }, //EMSGSIZE { 100, SocketError.NetworkDown }, //ENETDOWN { 102, SocketError.NetworkReset }, //ENETRESET { 101, SocketError.NetworkUnreachable }, //ENETUNREACH { 23, SocketError.TooManyOpenSockets }, //ENFILE { 105, SocketError.NoBufferSpaceAvailable }, //ENOBUFS { 61, SocketError.NoData }, //ENODATA { 2, SocketError.AddressNotAvailable }, //ENOENT { 92, SocketError.ProtocolOption }, //ENOPROTOOPT { 107, SocketError.NotConnected }, //ENOTCONN { 88, SocketError.NotSocket }, //ENOTSOCK { 3440, SocketError.OperationNotSupported }, //ENOTSUP { 1, SocketError.AccessDenied }, //EPERM { 32, SocketError.Shutdown }, //EPIPE { 96, SocketError.ProtocolFamilyNotSupported }, //EPFNOSUPPORT { 93, SocketError.ProtocolNotSupported }, //EPROTONOSUPPORT { 91, SocketError.ProtocolType }, //EPROTOTYPE { 94, SocketError.SocketNotSupported }, //ESOCKTNOSUPPORT { 108, SocketError.Disconnecting }, //ESHUTDOWN { 110, SocketError.TimedOut }, //ETIMEDOUT { 0, SocketError.Success } }; static NativeSocket() { if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) { IsSupported = true; UnixMode = true; } else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { IsSupported = true; } } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static int RecvFrom( IntPtr socketHandle, byte[] pinnedBuffer, int len, byte[] socketAddress, ref int socketAddressSize) { return UnixMode ? UnixSock.recvfrom(socketHandle, pinnedBuffer, len, 0, socketAddress, ref socketAddressSize) : WinSock.recvfrom(socketHandle, pinnedBuffer, len, 0, socketAddress, ref socketAddressSize); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public #if LITENETLIB_UNSAFE unsafe #endif static int SendTo( IntPtr socketHandle, #if LITENETLIB_UNSAFE byte* pinnedBuffer, #else byte[] pinnedBuffer, #endif int len, byte[] socketAddress, int socketAddressSize) { return UnixMode ? UnixSock.sendto(socketHandle, pinnedBuffer, len, 0, socketAddress, socketAddressSize) : WinSock.sendto(socketHandle, pinnedBuffer, len, 0, socketAddress, socketAddressSize); } public static SocketError GetSocketError() { int error = Marshal.GetLastWin32Error(); if (UnixMode) return NativeErrorToSocketError.TryGetValue(error, out var err) ? err : SocketError.SocketError; return (SocketError)error; } public static SocketException GetSocketException() { int error = Marshal.GetLastWin32Error(); if (UnixMode) return NativeErrorToSocketError.TryGetValue(error, out var err) ? new SocketException((int)err) : new SocketException((int)SocketError.SocketError); return new SocketException(error); } [MethodImpl(MethodImplOptions.AggressiveInlining)] public static short GetNativeAddressFamily(IPEndPoint remoteEndPoint) { return UnixMode ? (short)(remoteEndPoint.AddressFamily == AddressFamily.InterNetwork ? AF_INET : AF_INET6) : (short)remoteEndPoint.AddressFamily; } } }