Welcome to mirror list, hosted at ThFree Co, Russian Federation.

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteffen Kieß <s-kiess@web.de>2016-01-18 12:04:09 +0300
committerSteffen Kieß <s-kiess@web.de>2016-01-18 12:04:09 +0300
commitac52b7f4586d49e95f06aea93eb9405b6a2a4cbb (patch)
tree95148d6e16a489589cc05d497cc91731088fcada /mcs/class/Mono.Posix
parenta3e273b98b14bc2098b9f6171a0629bc94d92ef0 (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/class/Mono.Posix')
-rw-r--r--mcs/class/Mono.Posix/Makefile2
-rw-r--r--mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.cs110
-rw-r--r--mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs64
-rw-r--r--mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs862
-rw-r--r--mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs385
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]);
+ });
+ }
}
}