using System.Net; using System.Runtime.InteropServices; namespace Steamworks.Data { [StructLayout( LayoutKind.Explicit, Size = 18, Pack = 1 )] public partial struct NetAddress { [FieldOffset( 0 )] internal IPV4 ip; [FieldOffset( 16 )] internal ushort port; internal struct IPV4 { internal ulong m_8zeros; internal ushort m_0000; internal ushort m_ffff; internal byte ip0; internal byte ip1; internal byte ip2; internal byte ip3; } /// /// The Port. This is redundant documentation. /// public ushort Port => port; /// /// Any IP, specific port /// public static NetAddress AnyIp( ushort port ) { var addr = Cleared; addr.port = port; return addr; } /// /// Localhost IP, specific port /// public static NetAddress LocalHost( ushort port ) { var local = Cleared; InternalSetIPv6LocalHost( ref local, port ); return local; } /// /// Specific IP, specific port /// public static NetAddress From( string addrStr, ushort port ) { return From( IPAddress.Parse( addrStr ), port ); } /// /// Specific IP, specific port /// public static NetAddress From( IPAddress address, ushort port ) { var local = Cleared; switch (address.AddressFamily) { case System.Net.Sockets.AddressFamily.InterNetwork: InternalSetIPv4(ref local, address.IpToInt32(), port); return local; case System.Net.Sockets.AddressFamily.InterNetworkV6: var ptr = GetIpv6AddressBytesPtr(address); InternalSetIPv6(ref local, ptr, port); FreeMemory(ptr); return local; default: throw new System.NotImplementedException( $"Oops - no {address.AddressFamily} support yet?" ); } static System.IntPtr GetIpv6AddressBytesPtr(IPAddress address) { byte[] bytes = address.GetAddressBytes(); var unmanagedPointer = Marshal.AllocHGlobal(bytes.Length); Marshal.Copy(bytes, 0, unmanagedPointer, bytes.Length); return unmanagedPointer; } void FreeMemory(System.IntPtr unmanagedMemory) => Marshal.FreeHGlobal(unmanagedMemory); } /// /// Set everything to zero /// public static NetAddress Cleared { get { NetAddress self = default; InternalClear( ref self ); return self; } } /// /// Return true if the IP is ::0. (Doesn't check port.) /// public bool IsIPv6AllZeros { get { NetAddress self = this; return InternalIsIPv6AllZeros( ref self ); } } /// /// Return true if IP is mapped IPv4 /// public bool IsIPv4 { get { NetAddress self = this; return InternalIsIPv4( ref self ); } } /// /// Return true if IP is a fake IPv4 for Steam Datagram Relay /// public bool IsFakeIPv4 { get { NetAddress self = this; return SteamNetworkingUtils.Internal != null && SteamNetworkingUtils.Internal.IsFakeIPv4( InternalGetIPv4( ref self ) ); } } /// /// Return true if this identity is localhost. (Either IPv6 ::1, or IPv4 127.0.0.1) /// public bool IsLocalHost { get { NetAddress self = this; return InternalIsLocalHost( ref self ); } } /// /// Get the Address section /// public IPAddress Address { get { if ( IsIPv4 ) { NetAddress self = this; var ip = InternalGetIPv4( ref self ); return Utility.Int32ToIp( ip ); } if ( IsIPv6AllZeros ) { return IPAddress.IPv6Loopback; } throw new System.NotImplementedException( "Oops - no IPV6 support yet?" ); } } public override string ToString() { using var ptr = Helpers.TakeMemory(); var self = this; InternalToString( ref self, ptr, Helpers.MemoryBufferSize, true ); return Helpers.MemoryToString( ptr ); } } }