diff options
author | Steffen Kieß <s-kiess@web.de> | 2016-01-18 12:04:09 +0300 |
---|---|---|
committer | Steffen Kieß <s-kiess@web.de> | 2016-01-18 12:04:09 +0300 |
commit | ac52b7f4586d49e95f06aea93eb9405b6a2a4cbb (patch) | |
tree | 95148d6e16a489589cc05d497cc91731088fcada /mcs | |
parent | a3e273b98b14bc2098b9f6171a0629bc94d92ef0 (diff) |
[Mono.Posix] Add wrappers for struct sockaddr_*
Add wrappers for the sockaddr_* structures and add the syscalls using these
structures (bind(), accept(), ...).
Diffstat (limited to 'mcs')
-rw-r--r-- | mcs/class/Mono.Posix/Makefile | 2 | ||||
-rw-r--r-- | mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.cs | 110 | ||||
-rw-r--r-- | mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs | 64 | ||||
-rw-r--r-- | mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs | 862 | ||||
-rw-r--r-- | mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs | 385 |
5 files changed, 1421 insertions, 2 deletions
diff --git a/mcs/class/Mono.Posix/Makefile b/mcs/class/Mono.Posix/Makefile index fa0f5180187..9cde3498e4d 100644 --- a/mcs/class/Mono.Posix/Makefile +++ b/mcs/class/Mono.Posix/Makefile @@ -7,7 +7,7 @@ LIBRARY = Mono.Posix.dll # members, generating volumes of output. LIB_REFS = System LIB_MCS_FLAGS = /unsafe /r:$(corlib) /nowarn:0618,612 -TEST_MCS_FLAGS = /r:Mono.Posix.dll /r:System.dll /nowarn:0219,0618 +TEST_MCS_FLAGS = /unsafe /r:Mono.Posix.dll /r:System.dll /nowarn:0219,0618 LIBRARY_COMPILE = $(BOOT_COMPILE) diff --git a/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.cs b/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.cs index 4e2613b1279..57c03140112 100644 --- a/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.cs +++ b/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.cs @@ -359,7 +359,117 @@ namespace Mono.Unix.Native { { return ToStatvfs (source, out destination) == 0; } + + [DllImport (LIB, EntryPoint="Mono_Posix_FromInAddr")] + private static extern int FromInAddr (ref InAddr source, IntPtr destination); + + public static bool TryCopy (ref InAddr source, IntPtr destination) + { + return FromInAddr (ref source, destination) == 0; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_ToInAddr")] + private static extern int ToInAddr (IntPtr source, out InAddr destination); + + public static bool TryCopy (IntPtr source, out InAddr destination) + { + return ToInAddr (source, out destination) == 0; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_FromIn6Addr")] + private static extern int FromIn6Addr (ref In6Addr source, IntPtr destination); + + public static bool TryCopy (ref In6Addr source, IntPtr destination) + { + return FromIn6Addr (ref source, destination) == 0; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_ToIn6Addr")] + private static extern int ToIn6Addr (IntPtr source, out In6Addr destination); + + public static bool TryCopy (IntPtr source, out In6Addr destination) + { + return ToIn6Addr (source, out destination) == 0; + } + + public static InAddr ToInAddr (System.Net.IPAddress address) + { + if (address == null) + throw new ArgumentNullException ("address"); + if (address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork) + throw new ArgumentException ("address", "address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetwork"); + return new InAddr (address.GetAddressBytes ()); + } + + public static System.Net.IPAddress ToIPAddress (InAddr address) + { + var bytes = new byte[4]; + address.CopyTo (bytes, 0); + return new System.Net.IPAddress (bytes); + } + + public static In6Addr ToIn6Addr (System.Net.IPAddress address) + { + if (address == null) + throw new ArgumentNullException ("address"); + if (address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6) + throw new ArgumentException ("address", "address.AddressFamily != System.Net.Sockets.AddressFamily.InterNetworkV6"); + return new In6Addr (address.GetAddressBytes ()); + } + + public static System.Net.IPAddress ToIPAddress (In6Addr address) + { + var bytes = new byte[16]; + address.CopyTo (bytes, 0); + return new System.Net.IPAddress (bytes); + } + + [DllImport (LIB, EntryPoint="Mono_Posix_FromSockaddr")] + private static extern unsafe int FromSockaddr (_SockaddrHeader* source, IntPtr destination); + + public static unsafe bool TryCopy (Sockaddr source, IntPtr destination) + { + if (source == null) + throw new ArgumentNullException ("source"); + byte[] array = Sockaddr.GetDynamicData (source); + // SockaddrStorage has to be handled extra because the native code assumes that SockaddrStorage input is used in-place + if (source.type == (SockaddrType.SockaddrStorage | SockaddrType.MustBeWrapped)) { + Marshal.Copy (array, 0, destination, (int) source.GetDynamicLength ()); + return true; + } + fixed (SockaddrType* addr = &Sockaddr.GetAddress (source).type) + fixed (byte* data = array) { + var dyn = new _SockaddrDynamic (source, data, useMaxLength: false); + return FromSockaddr (Sockaddr.GetNative (&dyn, addr), destination) == 0; + } + } + + [DllImport (LIB, EntryPoint="Mono_Posix_ToSockaddr")] + private static extern unsafe int ToSockaddr (IntPtr source, long size, _SockaddrHeader* destination); + + public static unsafe bool TryCopy (IntPtr source, long size, Sockaddr destination) + { + if (destination == null) + throw new ArgumentNullException ("destination"); + byte[] array = Sockaddr.GetDynamicData (destination); + fixed (SockaddrType* addr = &Sockaddr.GetAddress (destination).type) + fixed (byte* data = Sockaddr.GetDynamicData (destination)) { + var dyn = new _SockaddrDynamic (destination, data, useMaxLength: true); + var r = ToSockaddr (source, size, Sockaddr.GetNative (&dyn, addr)); + dyn.Update (destination); + // SockaddrStorage has to be handled extra because the native code assumes that SockaddrStorage input is used in-place + if (r == 0 && destination.type == (SockaddrType.SockaddrStorage | SockaddrType.MustBeWrapped)) { + Marshal.Copy (source, array, 0, (int) destination.GetDynamicLength ()); + } + return r == 0; + } + } } } // vim: noexpandtab +// Local Variables: +// tab-width: 4 +// c-basic-offset: 4 +// indent-tabs-mode: t +// End: diff --git a/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs b/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs index 2ac13c6677e..436235334a1 100644 --- a/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs +++ b/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs @@ -918,6 +918,70 @@ namespace Mono.Unix.Native { return rval; } + [DllImport (LIB, EntryPoint="Mono_Posix_FromSockaddrIn")] + private static extern int FromSockaddrIn (SockaddrIn source, IntPtr destination); + + public static bool TryCopy (SockaddrIn source, IntPtr destination) + { + return FromSockaddrIn (source, destination) == 0; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_ToSockaddrIn")] + private static extern int ToSockaddrIn (IntPtr source, SockaddrIn destination); + + public static bool TryCopy (IntPtr source, SockaddrIn destination) + { + return ToSockaddrIn (source, destination) == 0; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_FromSockaddrIn6")] + private static extern int FromSockaddrIn6 (SockaddrIn6 source, IntPtr destination); + + public static bool TryCopy (SockaddrIn6 source, IntPtr destination) + { + return FromSockaddrIn6 (source, destination) == 0; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_ToSockaddrIn6")] + private static extern int ToSockaddrIn6 (IntPtr source, SockaddrIn6 destination); + + public static bool TryCopy (IntPtr source, SockaddrIn6 destination) + { + return ToSockaddrIn6 (source, destination) == 0; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_FromSockaddrType")] + private static extern int FromSockaddrType (SockaddrType value, out Int32 rval); + + internal static bool TryFromSockaddrType (SockaddrType value, out Int32 rval) + { + return FromSockaddrType (value, out rval) == 0; + } + + internal static Int32 FromSockaddrType (SockaddrType value) + { + Int32 rval; + if (FromSockaddrType (value, out rval) == -1) + ThrowArgumentException (value); + return rval; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_ToSockaddrType")] + private static extern int ToSockaddrType (Int32 value, out SockaddrType rval); + + internal static bool TryToSockaddrType (Int32 value, out SockaddrType rval) + { + return ToSockaddrType (value, out rval) == 0; + } + + internal static SockaddrType ToSockaddrType (Int32 value) + { + SockaddrType rval; + if (ToSockaddrType (value, out rval) == -1) + ThrowArgumentException (value); + return rval; + } + [DllImport (LIB, EntryPoint="Mono_Posix_FromSysconfName")] private static extern int FromSysconfName (SysconfName value, out Int32 rval); diff --git a/mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs b/mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs index 8b1246e617e..74af55b2fd6 100644 --- a/mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs +++ b/mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs @@ -821,6 +821,9 @@ namespace Mono.Unix.Native { AF_ALG = 38, /* Algorithm sockets. */ AF_NFC = 39, /* NFC sockets. */ AF_VSOCK = 40, /* vSockets. */ + + // Value used when a syscall returns an unknown address family value + Unknown = 65536, } [Map] @@ -907,6 +910,20 @@ namespace Mono.Unix.Native { SHUT_RDWR = 0x03, /* No more receptions or transmissions. */ } + // Used by libMonoPosixHelper to distinguish between different sockaddr types + [Map] + enum SockaddrType : int { + Invalid, + SockaddrStorage, + SockaddrUn, + Sockaddr, + SockaddrIn, + SockaddrIn6, + + // Flag to indicate that this Sockaddr must be wrapped with a _SockaddrDynamic wrapper + MustBeWrapped = 0x8000, + } + #endregion #region Structures @@ -1416,6 +1433,144 @@ namespace Mono.Unix.Native { public int l_linger; } + [Map] + [StructLayout (LayoutKind.Sequential)] + [CLSCompliant (false)] + public struct InAddr : IEquatable<InAddr> { + public uint s_addr; + + public unsafe InAddr (byte b0, byte b1, byte b2, byte b3) + { + s_addr = 0; + fixed (uint* ptr = &s_addr) { + byte* bytePtr = (byte*) ptr; + bytePtr[0] = b0; + bytePtr[1] = b1; + bytePtr[2] = b2; + bytePtr[3] = b3; + } + } + + public unsafe InAddr (byte[] buffer) + { + if (buffer.Length != 4) + throw new ArgumentException ("buffer.Length != 4", "buffer"); + s_addr = 0; + fixed (uint* ptr = &s_addr) + Marshal.Copy (buffer, 0, (IntPtr) ptr, 4); + } + + public unsafe void CopyFrom (byte[] source, int startIndex) + { + fixed (uint* ptr = &s_addr) + Marshal.Copy (source, startIndex, (IntPtr) ptr, 4); + } + + public unsafe void CopyTo (byte[] destination, int startIndex) + { + fixed (uint* ptr = &s_addr) + Marshal.Copy ((IntPtr) ptr, destination, startIndex, 4); + } + + public unsafe byte this[int index] { + get { + if (index < 0 || index >= 4) + throw new ArgumentOutOfRangeException ("index", "index < 0 || index >= 4"); + fixed (uint* ptr = &s_addr) + return ((byte*) ptr)[index]; + } + set { + if (index < 0 || index >= 4) + throw new ArgumentOutOfRangeException ("index", "index < 0 || index >= 4"); + fixed (uint* ptr = &s_addr) + ((byte*) ptr)[index] = value; + } + } + + public override string ToString () + { + return NativeConvert.ToIPAddress (this).ToString (); + } + + public override int GetHashCode () + { + return s_addr.GetHashCode (); + } + public override bool Equals (object obj) + { + if (!(obj is InAddr)) + return false; + return Equals ((InAddr) obj); + } + public bool Equals (InAddr value) + { + return s_addr == value.s_addr; + } + } + + [Map] + [StructLayout (LayoutKind.Sequential)] + public struct In6Addr : IEquatable<In6Addr> { + ulong addr0; + ulong addr1; + + public unsafe In6Addr (byte[] buffer) + { + if (buffer.Length != 16) + throw new ArgumentException ("buffer.Length != 16", "buffer"); + addr0 = addr1 = 0; + fixed (ulong* ptr = &addr0) + Marshal.Copy (buffer, 0, (IntPtr) ptr, 16); + } + + public unsafe void CopyFrom (byte[] source, int startIndex) + { + fixed (ulong* ptr = &addr0) + Marshal.Copy (source, startIndex, (IntPtr) ptr, 16); + } + + public unsafe void CopyTo (byte[] destination, int startIndex) + { + fixed (ulong* ptr = &addr0) + Marshal.Copy ((IntPtr) ptr, destination, startIndex, 16); + } + + public unsafe byte this[int index] { + get { + if (index < 0 || index >= 16) + throw new ArgumentOutOfRangeException ("index", "index < 0 || index >= 16"); + fixed (ulong* ptr = &addr0) + return ((byte*) ptr)[index]; + } + set { + if (index < 0 || index >= 16) + throw new ArgumentOutOfRangeException ("index", "index < 0 || index >= 16"); + fixed (ulong* ptr = &addr0) + ((byte*) ptr)[index] = value; + } + } + + public override string ToString () + { + return NativeConvert.ToIPAddress (this).ToString (); + } + + public override int GetHashCode () + { + return addr0.GetHashCode () ^ addr1.GetHashCode (); + } + public override bool Equals (object obj) + { + if (!(obj is In6Addr)) + return false; + return Equals ((In6Addr) obj); + } + public bool Equals (In6Addr value) + { + return addr0 == value.addr0 && addr1 == value.addr1; + } + } + #endregion #region Classes @@ -1710,6 +1865,522 @@ namespace Mono.Unix.Native { } } + // This struct is used by the native code. + // Its layout must be the same as the start of the Sockaddr class and the start of the _SockaddrDynamic struct + [Map] + [StructLayout (LayoutKind.Sequential)] + internal struct _SockaddrHeader { + internal SockaddrType type; + internal UnixAddressFamily sa_family; + } + + // Base class for all Sockaddr types. + // This class is not abstract, instances of this class can be used to determine the sa_family value. + // This class and all classes which are deriving from it and are passed to the native code have to be blittable. + [CLSCompliant (false)] + [StructLayout (LayoutKind.Sequential)] + public class Sockaddr { + // Note: the layout of the first members must match the layout of struct _SockaddrHeader + // 'type' must be the first field of the class as it is used to find the address of the class itself + internal SockaddrType type; + internal UnixAddressFamily _sa_family; + + public UnixAddressFamily sa_family { + get { return _sa_family; } + set { _sa_family = value; } + } + + public Sockaddr () + { + this.type = SockaddrType.Sockaddr; + this.sa_family = UnixAddressFamily.AF_UNSPEC; + } + + internal Sockaddr (SockaddrType type, UnixAddressFamily sa_family) + { + this.type = type; + this.sa_family = sa_family; + } + + [DllImport (Syscall.MPH, SetLastError=true, + EntryPoint="Mono_Posix_Sockaddr_GetNativeSize")] + static extern unsafe int GetNativeSize (_SockaddrHeader* address, out long size); + + internal unsafe long GetNativeSize () + { + long size; + fixed (SockaddrType* addr = &Sockaddr.GetAddress (this).type) + fixed (byte* data = Sockaddr.GetDynamicData (this)) { + var dyn = new _SockaddrDynamic (this, data, useMaxLength: false); + if (GetNativeSize (Sockaddr.GetNative (&dyn, addr), out size) != 0) + throw new ArgumentException ("Failed to get size of native struct", "this"); + } + return size; + } + + + // In order to create a wrapper for a syscall which accepts a "struct sockaddr" argument but does not modify it, use: + + // fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + // fixed (byte* data = Sockaddr.GetDynamicData (address)) { + // var dyn = new _SockaddrDynamic (address, data, useMaxLength: false); + // return sys_syscall (..., Sockaddr.GetNative (&dyn, addr)); + // } + + // For syscalls which modify the argument, use: + + // fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + // fixed (byte* data = Sockaddr.GetDynamicData (address)) { + // var dyn = new _SockaddrDynamic (address, data, useMaxLength: true); + // rettype r = sys_syscall (..., Sockaddr.GetNative (&dyn, addr)); + // dyn.Update (address); + // return r; + // } + + // This sequence will handle + // - normal Sockaddrs like SockaddrIn and SockaddrIn6 which will be passed directly, + // - sockaddrs like SockaddrUn and SockaddrStorage which need a wrapper and + // - null (which will be passed as null) + // without any heap memory allocations. + + + // This is a fake Sockaddr which is passed to the fixed() statement if the address was null. + // Sockaddr.GetNative() will return a null pointer for this Sockaddr. + static Sockaddr nullSockaddr = new Sockaddr (); + + internal static Sockaddr GetAddress (Sockaddr address) + { + if (address == null) + return nullSockaddr; + else + return address; + } + + internal static unsafe _SockaddrHeader* GetNative (_SockaddrDynamic* dyn, SockaddrType* addr) + { + if (dyn->data != null) { + return (_SockaddrHeader*) dyn; + } else { + fixed (SockaddrType* nullType = &nullSockaddr.type) + if (addr == nullType) + return null; + return (_SockaddrHeader*) addr; + } + } + + // Return an array containing the dynamic data (for SockaddrStorage and SockaddrUn) or null + internal static byte[] GetDynamicData (Sockaddr addr) + { + if (addr == null) + return null; + return addr.DynamicData (); + } + + // This methods is overwritten in SockaddrStorage and SockaddrUn + internal virtual byte[] DynamicData () + { + return null; + } + + // This methods should only be called for SockaddrStorage and SockaddrUn where they are overwritten + internal virtual long GetDynamicLength () + { + throw new NotImplementedException (); + } + + internal virtual void SetDynamicLength (long value) + { + throw new NotImplementedException (); + } + + public SockaddrStorage ToSockaddrStorage () + { + var storage = new SockaddrStorage ((int) GetNativeSize ()); + storage.SetTo (this); + return storage; + } + + public static Sockaddr FromSockaddrStorage (SockaddrStorage storage) + { + var ret = new Sockaddr (); + storage.CopyTo (ret); + return ret; + } + } + + // This struct is required to manually marshal Sockaddr* classes which include an array (currently SockaddrStorage and SockaddrUn). + // This is needed because the marshalling code will not work if the classes derived from Sockaddr aren't blittable. + [Map] + unsafe struct _SockaddrDynamic { + // Note: the layout of the first members must match the layout of struct _SockaddrHeader + public SockaddrType type; + public UnixAddressFamily sa_family; + public byte* data; + public long len; + + public _SockaddrDynamic (Sockaddr address, byte* data, bool useMaxLength) + { + if (data == null) { + // When data is null, no wrapper is needed. + // Initialize everything to zero, Sockaddr.GetNative() will then + // use the Sockaddr structure directly. + this = new _SockaddrDynamic (); + return; + } + + var dynData = address.DynamicData (); + + type = address.type & ~SockaddrType.MustBeWrapped; + sa_family = address.sa_family; + this.data = data; + if (useMaxLength) { + len = dynData.Length; + } else { + len = address.GetDynamicLength (); + if (len < 0 || len > dynData.Length) + throw new ArgumentException ("len < 0 || len > dynData.Length", "address"); + } + } + + public void Update (Sockaddr address) + { + // When data is null, no wrapper was needed. + if (data == null) + return; + + address.sa_family = sa_family; + address.SetDynamicLength (len); + } + }; + + // This is a class which can store arbitrary sockaddrs, even if they are not known the the Mono.Unix wrapper or the family does not have a corresponding value in the UnixAddressFamily enumeration. + [CLSCompliant (false)] + public sealed class SockaddrStorage : Sockaddr, IEquatable<SockaddrStorage> { + // Note: The sa_family field is ignored when passing a SockaddrStorage to a syscall (but it will be set when a SockaddrStorage is returned from a syscall). Instead of the sa_family field, the value embedded in data is used. + public byte[] data { get; set; } + public long data_len { get; set; } + + internal override byte[] DynamicData () + { + return data; + } + + internal override long GetDynamicLength () + { + return data_len; + } + + internal override void SetDynamicLength (long value) + { + data_len = value; + } + + [DllImport (Syscall.MPH, SetLastError=true, + EntryPoint="Mono_Posix_SockaddrStorage_get_size")] + static extern int get_size (); + static readonly int default_size = get_size (); + + public SockaddrStorage () + : base (SockaddrType.SockaddrStorage | SockaddrType.MustBeWrapped, UnixAddressFamily.AF_UNSPEC) + { + data = new byte[default_size]; + data_len = 0; + } + + public SockaddrStorage (int size) + : base (SockaddrType.SockaddrStorage | SockaddrType.MustBeWrapped, UnixAddressFamily.AF_UNSPEC) + { + data = new byte[size]; + data_len = 0; + } + + public unsafe void SetTo (Sockaddr address) + { + if (address == null) + throw new ArgumentNullException ("address"); + + var size = address.GetNativeSize (); + if (size > data.Length) + data = new byte[size]; + fixed (byte* ptr = data) + if (!NativeConvert.TryCopy (address, (IntPtr) ptr)) + throw new ArgumentException ("Failed to convert to native struct", "address"); + data_len = size; + sa_family = address.sa_family; + } + + public unsafe void CopyTo (Sockaddr address) + { + if (address == null) + throw new ArgumentNullException ("address"); + if (data_len < 0 || data_len > data.Length) + throw new ArgumentException ("data_len < 0 || data_len > data.Length", "this"); + + fixed (byte* ptr = data) + if (!NativeConvert.TryCopy ((IntPtr) ptr, data_len, address)) + throw new ArgumentException ("Failed to convert from native struct", "this"); + } + + public override string ToString () + { + var sb = new StringBuilder (); + sb.AppendFormat ("{{sa_family={0}, data_len={1}, data=(", sa_family, data_len); + for (int i = 0; i < data_len; i++) { + if (i != 0) + sb.Append (" "); + sb.Append (data[i].ToString ("x2")); + } + sb.Append (")"); + return sb.ToString (); + } + + public override int GetHashCode () + { + unchecked { + int hash = 0x1234; + for (int i = 0; i < data_len; i++) + hash += i ^ data[i]; + return hash; + } + } + + public override bool Equals (object obj) + { + if (!(obj is SockaddrStorage)) + return false; + return Equals ((SockaddrStorage) obj); + } + + public bool Equals (SockaddrStorage value) + { + if (value == null) + return false; + if (data_len != value.data_len) + return false; + for (int i = 0; i < data_len; i++) + if (data[i] != value.data[i]) + return false; + return true; + } + } + + [CLSCompliant (false)] + public sealed class SockaddrUn : Sockaddr, IEquatable<SockaddrUn> { + public UnixAddressFamily sun_family { // AF_UNIX + get { return sa_family; } + set { sa_family = value; } + } + public byte[] sun_path { get; set; } + public long sun_path_len { get; set; } // Indicates how many bytes of sun_path are valid. Must not be larger than sun_path.Length. + + internal override byte[] DynamicData () + { + return sun_path; + } + + internal override long GetDynamicLength () + { + return sun_path_len; + } + + internal override void SetDynamicLength (long value) + { + sun_path_len = value; + } + + [DllImport (Syscall.MPH, SetLastError=true, + EntryPoint="Mono_Posix_SockaddrUn_get_sizeof_sun_path")] + static extern int get_sizeof_sun_path (); + static readonly int sizeof_sun_path = get_sizeof_sun_path (); + + public SockaddrUn () + : base (SockaddrType.SockaddrUn | SockaddrType.MustBeWrapped, UnixAddressFamily.AF_UNIX) + { + sun_path = new byte[sizeof_sun_path]; + sun_path_len = 0; + } + + public SockaddrUn (int size) + : base (SockaddrType.SockaddrUn | SockaddrType.MustBeWrapped, UnixAddressFamily.AF_UNIX) + { + sun_path = new byte[size]; + sun_path_len = 0; + } + + public SockaddrUn (string path, bool linuxAbstractNamespace = false) + : base (SockaddrType.SockaddrUn | SockaddrType.MustBeWrapped, UnixAddressFamily.AF_UNIX) + { + if (path == null) + throw new ArgumentNullException ("path"); + var bytes = UnixEncoding.Instance.GetBytes (path); + if (linuxAbstractNamespace) { + sun_path = new byte[1 + bytes.Length]; + Array.Copy (bytes, 0, sun_path, 1, bytes.Length); + } else { + sun_path = bytes; + } + sun_path_len = sun_path.Length; + } + + public bool IsLinuxAbstractNamespace { + get { + return sun_path_len > 0 && sun_path[0] == 0; + } + } + + public string Path { + get { + var offset = IsLinuxAbstractNamespace ? 1 : 0; + // Remove data after null terminator + int length; + for (length = 0; offset + length < sun_path_len; length++) + if (sun_path[offset + length] == 0) + break; + return UnixEncoding.Instance.GetString (sun_path, offset, length); + } + } + + public override string ToString () + { + return string.Format ("{{sa_family={0}, sun_path=\"{1}{2}\"}}", sa_family, IsLinuxAbstractNamespace ? "\\0" : "", Path); + } + + public static new SockaddrUn FromSockaddrStorage (SockaddrStorage storage) + { + // This will make the SockaddrUn larger than it needs to be (because + // storage.data_len includes the sun_family field), but it will be + // large enough. + var ret = new SockaddrUn ((int) storage.data_len); + storage.CopyTo (ret); + return ret; + } + + public override int GetHashCode () + { + return sun_family.GetHashCode () ^ IsLinuxAbstractNamespace.GetHashCode () ^ Path.GetHashCode (); + } + + public override bool Equals (object obj) + { + if (!(obj is SockaddrUn)) + return false; + return Equals ((SockaddrUn) obj); + } + + public bool Equals (SockaddrUn value) + { + if (value == null) + return false; + return sun_family == value.sun_family + && IsLinuxAbstractNamespace == value.IsLinuxAbstractNamespace + && Path == value.Path; + } + } + + [Map ("struct sockaddr_in")] + [CLSCompliant (false)] + [StructLayout (LayoutKind.Sequential)] + public sealed class SockaddrIn : Sockaddr, IEquatable<SockaddrIn> { + public UnixAddressFamily sin_family { // AF_INET + get { return sa_family; } + set { sa_family = value; } + } + public ushort sin_port; // Port number. + public InAddr sin_addr; // IP address. + + public SockaddrIn () + : base (SockaddrType.SockaddrIn, UnixAddressFamily.AF_INET) + { + } + + public override string ToString () + { + return string.Format ("{{sin_family={0}, sin_port=htons({1}), sin_addr={2}}}", sa_family, Syscall.ntohs(sin_port), sin_addr); + } + + public static new SockaddrIn FromSockaddrStorage (SockaddrStorage storage) + { + var ret = new SockaddrIn (); + storage.CopyTo (ret); + return ret; + } + + public override int GetHashCode () + { + return sin_family.GetHashCode () ^ sin_port.GetHashCode () ^ sin_addr.GetHashCode (); + } + + public override bool Equals (object obj) + { + if (!(obj is SockaddrIn)) + return false; + return Equals ((SockaddrIn) obj); + } + + public bool Equals (SockaddrIn value) + { + if (value == null) + return false; + return sin_family == value.sin_family + && sin_port == value.sin_port + && sin_addr.Equals (value.sin_addr); + } + } + + [Map ("struct sockaddr_in6")] + [CLSCompliant (false)] + [StructLayout (LayoutKind.Sequential)] + public sealed class SockaddrIn6 : Sockaddr, IEquatable<SockaddrIn6> { + public UnixAddressFamily sin6_family { // AF_INET6 + get { return sa_family; } + set { sa_family = value; } + } + public ushort sin6_port; // Port number. + public uint sin6_flowinfo; // IPv6 traffic class and flow information. + public In6Addr sin6_addr; // IPv6 address. + public uint sin6_scope_id; // Set of interfaces for a scope. + + public SockaddrIn6 () + : base (SockaddrType.SockaddrIn6, UnixAddressFamily.AF_INET6) + { + } + + public override string ToString () + { + return string.Format ("{{sin6_family={0}, sin6_port=htons({1}), sin6_flowinfo={2}, sin6_addr={3}, sin6_scope_id={4}}}", sa_family, Syscall.ntohs (sin6_port), sin6_flowinfo, sin6_addr, sin6_scope_id); + } + + public static new SockaddrIn6 FromSockaddrStorage (SockaddrStorage storage) + { + var ret = new SockaddrIn6 (); + storage.CopyTo (ret); + return ret; + } + + public override int GetHashCode () + { + return sin6_family.GetHashCode () ^ sin6_port.GetHashCode () ^ sin6_flowinfo.GetHashCode () ^ sin6_addr.GetHashCode () ^ sin6_scope_id.GetHashCode (); + } + + public override bool Equals (object obj) + { + if (!(obj is SockaddrIn6)) + return false; + return Equals ((SockaddrIn6) obj); + } + + public bool Equals (SockaddrIn6 value) + { + if (value == null) + return false; + return sin6_family == value.sin6_family + && sin6_port == value.sin6_port + && sin6_flowinfo == value.sin6_flowinfo + && sin6_addr.Equals (value.sin6_addr) + && sin6_scope_id == value.sin6_scope_id; + } + } + // // Convention: Functions *not* part of the standard C library AND part of // a POSIX and/or Unix standard (X/Open, SUS, XPG, etc.) go here. @@ -4545,6 +5216,33 @@ namespace Mono.Unix.Native { } #endregion + #region <arpa/inet.h> Declarations + // + // <arpa/inet.h> + // + + // htonl(3) + // uint32_t htonl(uint32_t hostlong); + [DllImport (LIBC)] + public static extern uint htonl(uint hostlong); + + // htons(3) + // uint16_t htons(uint16_t hostshort); + [DllImport (LIBC)] + public static extern ushort htons(ushort hostshort); + + // ntohl(3) + // uint32_t ntohl(uint32_t netlong); + [DllImport (LIBC)] + public static extern uint ntohl(uint netlong); + + // ntohs(3) + // uint16_t ntohs(uint16_t netshort); + [DllImport (LIBC)] + public static extern ushort ntohs(ushort netshort); + + #endregion + #region <socket.h> Declarations // // <socket.h> @@ -4780,6 +5478,165 @@ namespace Mono.Unix.Native { return send (socket, ptr, length, flags); } + // bind(2) + // int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_bind")] + static extern unsafe int sys_bind (int socket, _SockaddrHeader* address); + + public static unsafe int bind (int socket, Sockaddr address) + { + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: false); + return sys_bind (socket, Sockaddr.GetNative (&dyn, addr)); + } + } + + // connect(2) + // int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_connect")] + static extern unsafe int sys_connect (int socket, _SockaddrHeader* address); + + public static unsafe int connect (int socket, Sockaddr address) + { + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: false); + return sys_connect (socket, Sockaddr.GetNative (&dyn, addr)); + } + } + + // accept(2) + // int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_accept")] + static extern unsafe int sys_accept (int socket, _SockaddrHeader* address); + + public static unsafe int accept (int socket, Sockaddr address) + { + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: true); + int r = sys_accept (socket, Sockaddr.GetNative (&dyn, addr)); + dyn.Update (address); + return r; + } + } + + // accept4(2) + // int accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_accept4")] + static extern unsafe int sys_accept4 (int socket, _SockaddrHeader* address, int flags); + + public static unsafe int accept4 (int socket, Sockaddr address, UnixSocketFlags flags) + { + var _flags = NativeConvert.FromUnixSocketFlags (flags); + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: true); + int r = sys_accept4 (socket, Sockaddr.GetNative (&dyn, addr), _flags); + dyn.Update (address); + return r; + } + } + + // getpeername(2) + // int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_getpeername")] + static extern unsafe int sys_getpeername (int socket, _SockaddrHeader* address); + + public static unsafe int getpeername (int socket, Sockaddr address) + { + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: true); + int r = sys_getpeername (socket, Sockaddr.GetNative (&dyn, addr)); + dyn.Update (address); + return r; + } + } + + // getsockname(2) + // int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_getsockname")] + static extern unsafe int sys_getsockname (int socket, _SockaddrHeader* address); + + public static unsafe int getsockname (int socket, Sockaddr address) + { + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: true); + int r = sys_getsockname (socket, Sockaddr.GetNative (&dyn, addr)); + dyn.Update (address); + return r; + } + } + + // recvfrom(2) + // ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_recvfrom")] + static extern unsafe long sys_recvfrom (int socket, void *buffer, ulong length, int flags, _SockaddrHeader* address); + + public static unsafe long recvfrom (int socket, void *buffer, ulong length, MessageFlags flags, Sockaddr address) + { + int _flags = NativeConvert.FromMessageFlags (flags); + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: true); + long r = sys_recvfrom (socket, buffer, length, _flags, Sockaddr.GetNative (&dyn, addr)); + dyn.Update (address); + return r; + } + } + + public static unsafe long recvfrom (int socket, IntPtr buffer, ulong length, MessageFlags flags, Sockaddr address) + { + return recvfrom (socket, (void*) buffer, length, flags, address); + } + + public static unsafe long recvfrom (int socket, byte[] buffer, ulong length, MessageFlags flags, Sockaddr address) + { + if (length > (ulong) buffer.LongLength) + throw new ArgumentOutOfRangeException ("length", "length > buffer.LongLength"); + fixed (byte* ptr = buffer) + return recvfrom (socket, ptr, length, flags, address); + } + + // sendto(2) + // ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_sendto")] + static extern unsafe long sys_sendto (int socket, void *message, ulong length, int flags, _SockaddrHeader* address); + + public static unsafe long sendto (int socket, void *message, ulong length, MessageFlags flags, Sockaddr address) + { + int _flags = NativeConvert.FromMessageFlags (flags); + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: false); + return sys_sendto (socket, message, length, _flags, Sockaddr.GetNative (&dyn, addr)); + } + } + + public static unsafe long sendto (int socket, IntPtr message, ulong length, MessageFlags flags, Sockaddr address) + { + return sendto (socket, (void*) message, length, flags, address); + } + + public static unsafe long sendto (int socket, byte[] message, ulong length, MessageFlags flags, Sockaddr address) + { + if (length > (ulong) message.LongLength) + throw new ArgumentOutOfRangeException ("length", "length > message.LongLength"); + fixed (byte* ptr = message) + return sendto (socket, ptr, length, flags, address); + } + #endregion } @@ -4787,3 +5644,8 @@ namespace Mono.Unix.Native { } // vim: noexpandtab +// Local Variables: +// tab-width: 4 +// c-basic-offset: 4 +// indent-tabs-mode: t +// End: diff --git a/mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs b/mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs index 36a8d233c17..aac0a58a688 100644 --- a/mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs +++ b/mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs @@ -98,7 +98,7 @@ namespace MonoTests.Mono.Unix.Native } [Test] - public void Socket () + public void TestSocket () { int socket; if ((socket = Syscall.socket (UnixAddressFamily.AF_UNIX, UnixSocketType.SOCK_STREAM, 0)) < 0) @@ -206,6 +206,389 @@ namespace MonoTests.Mono.Unix.Native Assert.AreEqual (ret, 0); }); } + + [Test] + public unsafe void ByteOrder () + { + ushort val1 = Syscall.htons (0x1234); + byte* ptr1 = (byte*) &val1; + Assert.AreEqual (ptr1[0], 0x12); + Assert.AreEqual (ptr1[1], 0x34); + + uint val2 = Syscall.htonl (0x6789abcd); + byte* ptr2 = (byte*) &val2; + Assert.AreEqual (ptr2[0], 0x67); + Assert.AreEqual (ptr2[1], 0x89); + Assert.AreEqual (ptr2[2], 0xab); + Assert.AreEqual (ptr2[3], 0xcd); + + ptr1[0] = 0xfe; + ptr1[1] = 0xdc; + Assert.AreEqual (Syscall.ntohs (val1), 0xfedc); + + ptr2[0] = 0x76; + ptr2[1] = 0x54; + ptr2[2] = 0x32; + ptr2[3] = 0x10; + Assert.AreEqual (Syscall.ntohl (val2), 0x76543210); + } + + [Test] + public void InAddr () + { + var ip = IPAddress.Loopback; + var inAddr = NativeConvert.ToInAddr (ip); + Assert.AreEqual (ip, NativeConvert.ToIPAddress (inAddr)); + Assert.AreEqual (0x7f000001, Syscall.ntohl (inAddr.s_addr)); + + Assert.AreEqual ("127.0.0.1", inAddr.ToString ()); + } + + [Test] + public void In6Addr () + { + if (!Socket.OSSupportsIPv6) + Assert.Ignore ("OS does not support IPv6."); + + var ip6 = IPAddress.IPv6Loopback; + var in6Addr = NativeConvert.ToIn6Addr (ip6); + Assert.AreEqual (ip6, NativeConvert.ToIPAddress (in6Addr)); + Assert.AreEqual (1, in6Addr[15]); + + Assert.AreEqual ("::1", in6Addr.ToString ()); + } + + [Test] + public void SockaddrUnTest () + { + var address1 = new SockaddrUn ("/tmp/foo"); + Assert.AreEqual (address1.Path, "/tmp/foo"); + Assert.IsFalse (address1.IsLinuxAbstractNamespace); + + var storage = address1.ToSockaddrStorage (); + var address2 = SockaddrUn.FromSockaddrStorage (storage); + Assert.AreEqual (address1, address2); + + var sockaddr = Sockaddr.FromSockaddrStorage (storage); + Assert.AreEqual (sockaddr.sa_family, address1.sa_family); + + var address3 = new SockaddrUn ("/tmp/bar", linuxAbstractNamespace:true); + Assert.AreEqual (address3.Path, "/tmp/bar"); + Assert.IsTrue (address3.IsLinuxAbstractNamespace); + + var address4 = new SockaddrUn (new string ('X', 9000)); + Assert.AreEqual (address4.Path, new string ('X', 9000)); + Assert.IsFalse (address4.IsLinuxAbstractNamespace); + var storage2 = address4.ToSockaddrStorage (); + var address5 = SockaddrUn.FromSockaddrStorage (storage2); + Assert.AreEqual (address4, address5); + // Test the malloc() path for long SockaddrUn adresses (the syscalls will fail because the fd is invalid and because the path is too long) + Syscall.bind (-1, address4); + Syscall.getsockname (-1, address4); + + Assert.AreEqual ("{sa_family=AF_UNIX, sun_path=\"/tmp/foo\"}", address1.ToString ()); + Assert.AreEqual ("{sa_family=AF_UNIX, sun_path=\"\\0/tmp/bar\"}", address3.ToString ()); + } + + [Test] + public void SockaddrInTest () + { + var address1 = new SockaddrIn { + sin_family = UnixAddressFamily.AF_INET, + sin_port = Syscall.htons (5678), + sin_addr = NativeConvert.ToInAddr (IPAddress.Loopback), + }; + + var storage = address1.ToSockaddrStorage (); + var address2 = SockaddrIn.FromSockaddrStorage (storage); + Assert.AreEqual (address1, address2); + + var sockaddr = Sockaddr.FromSockaddrStorage (storage); + Assert.AreEqual (sockaddr.sa_family, address1.sa_family); + + var storage2 = storage.ToSockaddrStorage (); + Assert.AreEqual (storage, storage2); + + var storage3 = new SockaddrStorage (123); + storage2.CopyTo (storage3); + Assert.AreEqual (storage, storage3); + + Assert.AreEqual ("{sin_family=AF_INET, sin_port=htons(5678), sin_addr=127.0.0.1}", address1.ToString ()); + } + + [Test] + public void SockaddrIn6Test () + { + if (!Socket.OSSupportsIPv6) + Assert.Ignore ("OS does not support IPv6."); + + var address1 = new SockaddrIn6 { + sin6_family = UnixAddressFamily.AF_INET6, + sin6_port = Syscall.htons (1234), + sin6_flowinfo = 2, + sin6_addr = NativeConvert.ToIn6Addr (IPAddress.IPv6Loopback), + sin6_scope_id = 3 + }; + + var storage = address1.ToSockaddrStorage (); + var address2 = SockaddrIn6.FromSockaddrStorage (storage); + Assert.AreEqual (address1, address2); + + var sockaddr = Sockaddr.FromSockaddrStorage (storage); + Assert.AreEqual (sockaddr.sa_family, address1.sa_family); + + Assert.AreEqual ("{sin6_family=AF_INET6, sin6_port=htons(1234), sin6_flowinfo=2, sin6_addr=::1, sin6_scope_id=3}", address1.ToString ()); + } + + [Test] + public void BindConnect () + { + WithSockets (UnixAddressFamily.AF_INET, UnixSocketType.SOCK_DGRAM, UnixSocketProtocol.IPPROTO_UDP, (so1, so2) => { + // Bind UDP socket so1 to 127.0.0.1 with dynamic port + var address = new SockaddrIn { + sin_family = UnixAddressFamily.AF_INET, + sin_port = Syscall.htons (0), + sin_addr = new InAddr (127, 0, 0, 1), + }; + if (Syscall.bind (so1, address) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + // Get actual port number using getsockname() + var actualAddress = new SockaddrIn (); + if (Syscall.getsockname (so1, actualAddress) < 0) + UnixMarshal.ThrowExceptionForLastError (); + Assert.AreEqual (actualAddress.sa_family, UnixAddressFamily.AF_INET); + var port = Syscall.ntohs (actualAddress.sin_port); + Assert.IsTrue (port != 0); + + + // Connect so2 to so1 + var remoteAddress = new SockaddrIn { + sin_family = UnixAddressFamily.AF_INET, + sin_port = Syscall.htons (port), + sin_addr = new InAddr (127, 0, 0, 1), + }; + if (Syscall.connect (so2, remoteAddress) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + // Verify peer address using getpeername() + var address2 = new SockaddrIn (); + if (Syscall.getpeername (so2, address2) < 0) + UnixMarshal.ThrowExceptionForLastError (); + Assert.AreEqual (address2.sa_family, UnixAddressFamily.AF_INET); + Assert.AreEqual (remoteAddress.sin_port, address2.sin_port); + Assert.AreEqual (remoteAddress.sin_addr, address2.sin_addr); + + // Send and receive a few bytes + long ret; + var buffer1 = new byte[] { 42, 43, 44 }; + ret = Syscall.send (so2, buffer1, (ulong) buffer1.Length, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var buffer2 = new byte[1024]; + ret = Syscall.recv (so1, buffer2, (ulong) buffer2.Length, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + Assert.AreEqual (buffer1.Length, ret); + for (int i = 0; i < buffer1.Length; i++) + Assert.AreEqual (buffer1[i], buffer2[i]); + }); + } + + [Test] + public void IPv6 () + { + if (!Socket.OSSupportsIPv6) + Assert.Ignore ("OS does not support IPv6."); + + var address = new SockaddrIn6 { + sin6_family = UnixAddressFamily.AF_INET6, + sin6_port = Syscall.htons (0), + sin6_addr = NativeConvert.ToIn6Addr (IPAddress.IPv6Loopback), + }; + WithSockets (UnixAddressFamily.AF_INET6, UnixSocketType.SOCK_STREAM, 0, (so1, so2) => { + if (Syscall.bind (so1, address) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var address1Stor = new SockaddrStorage (); + if (Syscall.getsockname (so1, address1Stor) < 0) + UnixMarshal.ThrowExceptionForLastError (); + var address1 = new SockaddrIn6 (); + address1Stor.CopyTo (address1); + + // Check getsockname(socket, null) + if (Syscall.getsockname (so1, null) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var address2 = new SockaddrIn6 (); + if (Syscall.getsockname (so1, address2) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + Assert.AreEqual (address1, address2); + Assert.IsTrue (Syscall.ntohs (address1.sin6_port) != 0); + address1.sin6_port = 0; + Assert.AreEqual (address, address1); + + var address3 = new Sockaddr (); + if (Syscall.getsockname (so1, address3) < 0) + UnixMarshal.ThrowExceptionForLastError (); + Assert.AreEqual (address.sa_family, address3.sa_family); + + // Try to store a sockaddr_in6 into a Sockaddr. Should fail because sockaddr_in6 should be larger than sockaddr_in + var address4 = new SockaddrIn (); + if (Syscall.getsockname (so1, address4) == 0) + Assert.Fail ("getsockname() should have failed"); + Assert.AreEqual (Errno.ENOBUFS, Stdlib.GetLastError ()); + }); + } + + [Test] + public void UnixAccept () + { + var address = new SockaddrUn (TempFolder + "/socket1"); + var address2 = SockaddrUn.FromSockaddrStorage (address.ToSockaddrStorage ()); + Assert.AreEqual (address, address2); + + WithSockets (UnixAddressFamily.AF_UNIX, UnixSocketType.SOCK_STREAM, 0, (so1, so2) => { + if (Syscall.bind (so1, address) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + if (Syscall.listen (so1, 5) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + if (Syscall.connect (so2, address) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var address3 = new SockaddrUn (); + if (Syscall.getsockname (so1, address3) < 0) + UnixMarshal.ThrowExceptionForLastError (); + Assert.AreEqual (address, address3); + + var address4 = new SockaddrStorage (); + if (Syscall.getsockname (so1, address4) < 0) + UnixMarshal.ThrowExceptionForLastError (); + Assert.AreEqual (UnixAddressFamily.AF_UNIX, address4.sa_family); + Assert.AreEqual (address3, SockaddrUn.FromSockaddrStorage (address4)); + + var address5 = new SockaddrUn (); + if (Syscall.getsockname (so1, address5) < 0) + UnixMarshal.ThrowExceptionForLastError (); + Assert.AreEqual (UnixAddressFamily.AF_UNIX, address5.sa_family); + + // Check getsockname(socket, null) + if (Syscall.getsockname (so1, null) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + int so3; + var remote = new SockaddrUn (); + if ((so3 = Syscall.accept (so1, remote)) < 0) + UnixMarshal.ThrowExceptionForLastError (); + try { + // Send and receive a few bytes + long ret; + var buffer1 = new byte[] { 42, 43, 44 }; + ret = Syscall.send (so2, buffer1, (ulong) buffer1.Length, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var buffer2 = new byte[1024]; + ret = Syscall.recv (so3, buffer2, (ulong) buffer2.Length, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + Assert.AreEqual (buffer1.Length, ret); + for (int i = 0; i < buffer1.Length; i++) + Assert.AreEqual (buffer1[i], buffer2[i]); + } finally { + if (Syscall.close (so3) < 0) + UnixMarshal.ThrowExceptionForLastError (); + } + }); + } + + [Test] + public void Accept4 () + { + WithSockets (UnixAddressFamily.AF_UNIX, UnixSocketType.SOCK_STREAM, 0, (so1, so2) => { + var address = new SockaddrUn (TempFolder + "/socket2"); + if (Syscall.bind (so1, address) < 0) + UnixMarshal.ThrowExceptionForLastError (); + if (Syscall.listen (so1, 5) < 0) + UnixMarshal.ThrowExceptionForLastError (); + if (Syscall.connect (so2, address) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + int so3; + var remote = new SockaddrUn (); + if ((so3 = Syscall.accept4 (so1, remote, UnixSocketFlags.SOCK_CLOEXEC | UnixSocketFlags.SOCK_NONBLOCK)) < 0) + UnixMarshal.ThrowExceptionForLastError (); + try { + int _flags; + if ((_flags = Syscall.fcntl (so3, FcntlCommand.F_GETFL)) < 0) + UnixMarshal.ThrowExceptionForLastError (); + var flags = NativeConvert.ToOpenFlags (_flags); + Assert.IsTrue ((flags & OpenFlags.O_NONBLOCK) != 0); + + int _flagsFD; + if ((_flagsFD = Syscall.fcntl (so3, FcntlCommand.F_GETFD)) < 0) + UnixMarshal.ThrowExceptionForLastError (); + // FD_CLOEXEC must be set + //var flagsFD = NativeConvert.ToFdFlags (_flagsFD); + //Assert.IsTrue ((flagsFD & FdFlags.FD_CLOEXEC) != 0); + Assert.IsTrue (_flagsFD != 0); + } finally { + if (Syscall.close (so3) < 0) + UnixMarshal.ThrowExceptionForLastError (); + } + }); + } + + [Test] + public void SendToRecvFrom () + { + WithSockets (UnixAddressFamily.AF_INET, UnixSocketType.SOCK_DGRAM, UnixSocketProtocol.IPPROTO_UDP, (so1, so2) => { + // Bind UDP socket so1 to 127.0.0.1 with dynamic port + var address = new SockaddrIn { sin_family = UnixAddressFamily.AF_INET, sin_port = Syscall.htons (0), sin_addr = new InAddr (127, 0, 0, 1) }; + if (Syscall.bind (so1, address) < 0) + UnixMarshal.ThrowExceptionForLastError (); + + // Get actual port number using getsockname() + var actualAddress = new SockaddrIn (); + if (Syscall.getsockname (so1, actualAddress) < 0) + UnixMarshal.ThrowExceptionForLastError (); + Assert.AreEqual (actualAddress.sa_family, UnixAddressFamily.AF_INET); + var port = Syscall.ntohs (actualAddress.sin_port); + Assert.IsTrue (port != 0); + + + var remoteAddress = new SockaddrIn { + sin_family = UnixAddressFamily.AF_INET, + sin_port = Syscall.htons (port), + sin_addr = new InAddr (127, 0, 0, 1), + }; + + // Send and receive a few bytes + long ret; + var buffer1 = new byte[] { 42, 43, 44 }; + ret = Syscall.sendto (so2, buffer1, (ulong) buffer1.Length, 0, remoteAddress); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var senderAddress = new SockaddrIn (); + var buffer2 = new byte[1024]; + ret = Syscall.recvfrom (so1, buffer2, (ulong) buffer2.Length, 0, senderAddress); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + Assert.AreEqual (senderAddress.sa_family, UnixAddressFamily.AF_INET); + Assert.AreEqual (senderAddress.sin_addr, new InAddr (127, 0, 0, 1)); + + Assert.AreEqual (buffer1.Length, ret); + for (int i = 0; i < buffer1.Length; i++) + Assert.AreEqual (buffer1[i], buffer2[i]); + }); + } } } |