From ac52b7f4586d49e95f06aea93eb9405b6a2a4cbb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Kie=C3=9F?= Date: Mon, 18 Jan 2016 10:04:09 +0100 Subject: [PATCH] [Mono.Posix] Add wrappers for struct sockaddr_* Add wrappers for the sockaddr_* structures and add the syscalls using these structures (bind(), accept(), ...). --- configure.ac | 10 + mcs/class/Mono.Posix/Makefile | 2 +- .../Mono.Unix.Native/NativeConvert.cs | 110 +++ .../NativeConvert.generated.cs | 64 ++ .../Mono.Posix/Mono.Unix.Native/Syscall.cs | 862 ++++++++++++++++++ .../Test/Mono.Unix.Native/SocketTest.cs | 385 +++++++- support/Makefile.am | 2 +- support/map.c | 178 +++- support/map.h | 113 ++- support/sys-socket.c | 376 ++++++++ 10 files changed, 2089 insertions(+), 13 deletions(-) diff --git a/configure.ac b/configure.ac index e26a851de63b8..11dba9d461bd3 100644 --- a/configure.ac +++ b/configure.ac @@ -2092,6 +2092,16 @@ if test x$host_win32 = xno; then [#include ]) AC_CHECK_TYPES([struct pollfd], [AC_DEFINE(HAVE_STRUCT_POLLFD)], , [#include ]) + AC_CHECK_TYPES([struct sockaddr], [AC_DEFINE(HAVE_STRUCT_SOCKADDR)], , + [#include ]) + AC_CHECK_TYPES([struct sockaddr_storage], [AC_DEFINE(HAVE_STRUCT_SOCKADDR_STORAGE)], , + [#include ]) + AC_CHECK_TYPES([struct sockaddr_in], [AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN)], , + [#include ]) + AC_CHECK_TYPES([struct sockaddr_in6], [AC_DEFINE(HAVE_STRUCT_SOCKADDR_IN6)], , + [#include ]) + AC_CHECK_TYPES([struct sockaddr_un], [AC_DEFINE(HAVE_STRUCT_SOCKADDR_UN)], , + [#include ]) AC_CHECK_TYPES([struct stat], [AC_DEFINE(HAVE_STRUCT_STAT)], , [#include #include diff --git a/mcs/class/Mono.Posix/Makefile b/mcs/class/Mono.Posix/Makefile index fa0f5180187f5..9cde3498e4d17 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 4e2613b127991..57c03140112b7 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 @@ public static bool TryCopy (IntPtr source, out Statvfs destination) { 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 2ac13c6677edd..436235334a157 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 @@ public static Signum ToSignum (Int32 value) 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 8b1246e617eb8..74af55b2fd6c2 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 @@ public enum UnixAddressFamily : int { 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 @@ public enum ShutdownOption : int { 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 @@ public struct Linger { public int l_linger; } + [Map] + [StructLayout (LayoutKind.Sequential)] + [CLSCompliant (false)] + public struct InAddr : IEquatable { + 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 { + 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 @@ public override string ToString () } } + // 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 { + // 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 { + 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 { + 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 { + 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 @@ public static long pwritev (int fd, Iovec[] iov, long offset) } #endregion + #region Declarations + // + // + // + + // 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 Declarations // // @@ -4780,6 +5478,165 @@ public static unsafe long send (int socket, byte[] message, ulong length, Messag 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 @@ public static unsafe long send (int socket, byte[] message, ulong length, Messag } // 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 36a8d233c176c..aac0a58a688e9 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 @@ void WithSockets (UnixAddressFamily af, UnixSocketType type, UnixSocketProtocol } [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 @@ public void Shutdown () 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]); + }); + } } } diff --git a/support/Makefile.am b/support/Makefile.am index 79154fc99bbd9..be3864cc9dab1 100644 --- a/support/Makefile.am +++ b/support/Makefile.am @@ -161,7 +161,7 @@ refresh: --rename-member=st_mtime=st_mtime_ \ --rename-namespace=Mono.Unix.Native=Mono.Posix \ --library=MonoPosixHelper \ - $(mcs_topdir)/class/lib/net_4_x/Mono.Posix.dll map + $(mcs_topdir_from_srcdir)/class/lib/net_4_x/Mono.Posix.dll map # Useful if mono is compiled with --enable-shared=no patch-libtool: diff --git a/support/map.c b/support/map.c index ee61ae661d9be..6201c260c9336 100644 --- a/support/map.c +++ b/support/map.c @@ -1,5 +1,5 @@ /* - * This file was automatically generated by create-native-map from /home/kiesssn/prog/mono/mono/mcs/class/lib/net_4_x/Mono.Posix.dll. + * This file was automatically generated by create-native-map from ../mcs/class/lib/net_4_x/Mono.Posix.dll. * * DO NOT MODIFY. */ @@ -5149,6 +5149,172 @@ int Mono_Posix_ToSignum (int x, int *r) errno = EINVAL; return -1; } +#ifdef HAVE_STRUCT_SOCKADDR_IN +int +Mono_Posix_FromSockaddrIn (struct Mono_Posix_SockaddrIn *from, struct sockaddr_in *to) +{ + _cnm_return_val_if_overflow (unsigned short, from->sin_port, -1); + + memset (to, 0, sizeof(*to)); + + to->sin_port = from->sin_port; + if (Mono_Posix_FromInAddr (&from->sin_addr, &to->sin_addr) != 0) { + return -1; + } + + return 0; +} +#endif /* ndef HAVE_STRUCT_SOCKADDR_IN */ + + +#ifdef HAVE_STRUCT_SOCKADDR_IN +int +Mono_Posix_ToSockaddrIn (struct sockaddr_in *from, struct Mono_Posix_SockaddrIn *to) +{ + _cnm_return_val_if_overflow (unsigned short, from->sin_port, -1); + + memset (to, 0, sizeof(*to)); + + to->sin_port = from->sin_port; + if (Mono_Posix_ToInAddr (&from->sin_addr, &to->sin_addr) != 0) { + return -1; + } + + return 0; +} +#endif /* ndef HAVE_STRUCT_SOCKADDR_IN */ + + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 +int +Mono_Posix_FromSockaddrIn6 (struct Mono_Posix_SockaddrIn6 *from, struct sockaddr_in6 *to) +{ + _cnm_return_val_if_overflow (unsigned short, from->sin6_port, -1); + _cnm_return_val_if_overflow (unsigned int, from->sin6_flowinfo, -1); + _cnm_return_val_if_overflow (unsigned int, from->sin6_scope_id, -1); + + memset (to, 0, sizeof(*to)); + + to->sin6_port = from->sin6_port; + to->sin6_flowinfo = from->sin6_flowinfo; + if (Mono_Posix_FromIn6Addr (&from->sin6_addr, &to->sin6_addr) != 0) { + return -1; + } + to->sin6_scope_id = from->sin6_scope_id; + + return 0; +} +#endif /* ndef HAVE_STRUCT_SOCKADDR_IN6 */ + + +#ifdef HAVE_STRUCT_SOCKADDR_IN6 +int +Mono_Posix_ToSockaddrIn6 (struct sockaddr_in6 *from, struct Mono_Posix_SockaddrIn6 *to) +{ + _cnm_return_val_if_overflow (unsigned short, from->sin6_port, -1); + _cnm_return_val_if_overflow (unsigned int, from->sin6_flowinfo, -1); + _cnm_return_val_if_overflow (unsigned int, from->sin6_scope_id, -1); + + memset (to, 0, sizeof(*to)); + + to->sin6_port = from->sin6_port; + to->sin6_flowinfo = from->sin6_flowinfo; + if (Mono_Posix_ToIn6Addr (&from->sin6_addr, &to->sin6_addr) != 0) { + return -1; + } + to->sin6_scope_id = from->sin6_scope_id; + + return 0; +} +#endif /* ndef HAVE_STRUCT_SOCKADDR_IN6 */ + + +int Mono_Posix_FromSockaddrType (int x, int *r) +{ + *r = 0; + if (x == Mono_Posix_SockaddrType_Invalid) +#ifdef Invalid + {*r = Invalid; return 0;} +#else /* def Invalid */ + {errno = EINVAL; return -1;} +#endif /* ndef Invalid */ + if (x == Mono_Posix_SockaddrType_MustBeWrapped) +#ifdef MustBeWrapped + {*r = MustBeWrapped; return 0;} +#else /* def MustBeWrapped */ + {errno = EINVAL; return -1;} +#endif /* ndef MustBeWrapped */ + if (x == Mono_Posix_SockaddrType_Sockaddr) +#ifdef Sockaddr + {*r = Sockaddr; return 0;} +#else /* def Sockaddr */ + {errno = EINVAL; return -1;} +#endif /* ndef Sockaddr */ + if (x == Mono_Posix_SockaddrType_SockaddrIn) +#ifdef SockaddrIn + {*r = SockaddrIn; return 0;} +#else /* def SockaddrIn */ + {errno = EINVAL; return -1;} +#endif /* ndef SockaddrIn */ + if (x == Mono_Posix_SockaddrType_SockaddrIn6) +#ifdef SockaddrIn6 + {*r = SockaddrIn6; return 0;} +#else /* def SockaddrIn6 */ + {errno = EINVAL; return -1;} +#endif /* ndef SockaddrIn6 */ + if (x == Mono_Posix_SockaddrType_SockaddrStorage) +#ifdef SockaddrStorage + {*r = SockaddrStorage; return 0;} +#else /* def SockaddrStorage */ + {errno = EINVAL; return -1;} +#endif /* ndef SockaddrStorage */ + if (x == Mono_Posix_SockaddrType_SockaddrUn) +#ifdef SockaddrUn + {*r = SockaddrUn; return 0;} +#else /* def SockaddrUn */ + {errno = EINVAL; return -1;} +#endif /* ndef SockaddrUn */ + if (x == 0) + return 0; + errno = EINVAL; return -1; +} + +int Mono_Posix_ToSockaddrType (int x, int *r) +{ + *r = 0; + if (x == 0) + return 0; +#ifdef Invalid + if (x == Invalid) + {*r = Mono_Posix_SockaddrType_Invalid; return 0;} +#endif /* ndef Invalid */ +#ifdef MustBeWrapped + if (x == MustBeWrapped) + {*r = Mono_Posix_SockaddrType_MustBeWrapped; return 0;} +#endif /* ndef MustBeWrapped */ +#ifdef Sockaddr + if (x == Sockaddr) + {*r = Mono_Posix_SockaddrType_Sockaddr; return 0;} +#endif /* ndef Sockaddr */ +#ifdef SockaddrIn + if (x == SockaddrIn) + {*r = Mono_Posix_SockaddrType_SockaddrIn; return 0;} +#endif /* ndef SockaddrIn */ +#ifdef SockaddrIn6 + if (x == SockaddrIn6) + {*r = Mono_Posix_SockaddrType_SockaddrIn6; return 0;} +#endif /* ndef SockaddrIn6 */ +#ifdef SockaddrStorage + if (x == SockaddrStorage) + {*r = Mono_Posix_SockaddrType_SockaddrStorage; return 0;} +#endif /* ndef SockaddrStorage */ +#ifdef SockaddrUn + if (x == SockaddrUn) + {*r = Mono_Posix_SockaddrType_SockaddrUn; return 0;} +#endif /* ndef SockaddrUn */ + errno = EINVAL; return -1; +} + int Mono_Posix_FromSysconfName (int x, int *r) { *r = 0; @@ -7902,6 +8068,12 @@ int Mono_Posix_FromUnixAddressFamily (int x, int *r) #else /* def AF_X25 */ {errno = EINVAL; return -1;} #endif /* ndef AF_X25 */ + if (x == Mono_Posix_UnixAddressFamily_Unknown) +#ifdef Unknown + {*r = Unknown; return 0;} +#else /* def Unknown */ + {errno = EINVAL; return -1;} +#endif /* ndef Unknown */ if (x == 0) return 0; errno = EINVAL; return -1; @@ -8068,6 +8240,10 @@ int Mono_Posix_ToUnixAddressFamily (int x, int *r) if (x == AF_X25) {*r = Mono_Posix_UnixAddressFamily_AF_X25; return 0;} #endif /* ndef AF_X25 */ +#ifdef Unknown + if (x == Unknown) + {*r = Mono_Posix_UnixAddressFamily_Unknown; return 0;} +#endif /* ndef Unknown */ errno = EINVAL; return -1; } diff --git a/support/map.h b/support/map.h index 34cdb68ab4da8..eb5bff9ec9aef 100644 --- a/support/map.h +++ b/support/map.h @@ -1018,6 +1018,25 @@ enum Mono_Posix_Signum { int Mono_Posix_FromSignum (int x, int *r); int Mono_Posix_ToSignum (int x, int *r); +enum Mono_Posix_SockaddrType { + Mono_Posix_SockaddrType_Invalid = 0x00000000, + #define Mono_Posix_SockaddrType_Invalid Mono_Posix_SockaddrType_Invalid + Mono_Posix_SockaddrType_MustBeWrapped = 0x00008000, + #define Mono_Posix_SockaddrType_MustBeWrapped Mono_Posix_SockaddrType_MustBeWrapped + Mono_Posix_SockaddrType_Sockaddr = 0x00000003, + #define Mono_Posix_SockaddrType_Sockaddr Mono_Posix_SockaddrType_Sockaddr + Mono_Posix_SockaddrType_SockaddrIn = 0x00000004, + #define Mono_Posix_SockaddrType_SockaddrIn Mono_Posix_SockaddrType_SockaddrIn + Mono_Posix_SockaddrType_SockaddrIn6 = 0x00000005, + #define Mono_Posix_SockaddrType_SockaddrIn6 Mono_Posix_SockaddrType_SockaddrIn6 + Mono_Posix_SockaddrType_SockaddrStorage = 0x00000001, + #define Mono_Posix_SockaddrType_SockaddrStorage Mono_Posix_SockaddrType_SockaddrStorage + Mono_Posix_SockaddrType_SockaddrUn = 0x00000002, + #define Mono_Posix_SockaddrType_SockaddrUn Mono_Posix_SockaddrType_SockaddrUn +}; +int Mono_Posix_FromSockaddrType (int x, int *r); +int Mono_Posix_ToSockaddrType (int x, int *r); + enum Mono_Posix_SysconfName { Mono_Posix_SysconfName__SC_2_CHAR_TERM = 0x0000005f, #define Mono_Posix_SysconfName__SC_2_CHAR_TERM Mono_Posix_SysconfName__SC_2_CHAR_TERM @@ -1587,6 +1606,8 @@ enum Mono_Posix_UnixAddressFamily { #define Mono_Posix_UnixAddressFamily_AF_WANPIPE Mono_Posix_UnixAddressFamily_AF_WANPIPE Mono_Posix_UnixAddressFamily_AF_X25 = 0x00000009, #define Mono_Posix_UnixAddressFamily_AF_X25 Mono_Posix_UnixAddressFamily_AF_X25 + Mono_Posix_UnixAddressFamily_Unknown = 0x00010000, + #define Mono_Posix_UnixAddressFamily_Unknown Mono_Posix_UnixAddressFamily_Unknown }; int Mono_Posix_FromUnixAddressFamily (int x, int *r); int Mono_Posix_ToUnixAddressFamily (int x, int *r); @@ -1799,9 +1820,13 @@ int Mono_Posix_ToXattrFlags (int x, int *r); */ struct Mono_Posix_Flock; +struct Mono_Posix_In6Addr; +struct Mono_Posix_InAddr; struct Mono_Posix_Iovec; struct Mono_Posix_Linger; struct Mono_Posix_Pollfd; +struct Mono_Posix_SockaddrIn; +struct Mono_Posix_SockaddrIn6; struct Mono_Posix_Stat; struct Mono_Posix_Statvfs; struct Mono_Posix_Syscall__Dirent; @@ -1813,6 +1838,8 @@ struct Mono_Posix_Timespec; struct Mono_Posix_Timeval; struct Mono_Posix_Timezone; struct Mono_Posix_Utimbuf; +struct Mono_Posix__SockaddrDynamic; +struct Mono_Posix__SockaddrHeader; struct Mono_Unix_UnixSignal_SignalInfo; /* @@ -1823,6 +1850,8 @@ struct flock; struct iovec; struct linger; struct pollfd; +struct sockaddr_in; +struct sockaddr_in6; struct timespec; struct timeval; struct timezone; @@ -1852,6 +1881,15 @@ int Mono_Posix_ToFlock (struct flock *from, struct Mono_Posix_Flock* to); +struct Mono_Posix_In6Addr { + guint64 addr0; + guint64 addr1; +}; + +struct Mono_Posix_InAddr { + unsigned int s_addr; +}; + struct Mono_Posix_Iovec { void* iov_base; guint64 iov_len; @@ -1886,6 +1924,34 @@ int Mono_Posix_ToPollfd (struct pollfd *from, struct Mono_Posix_Pollfd* to); +struct Mono_Posix_SockaddrIn { + int type; + int _sa_family; + unsigned short sin_port; + struct Mono_Posix_InAddr sin_addr; +}; + +int +Mono_Posix_FromSockaddrIn (struct Mono_Posix_SockaddrIn* from, struct sockaddr_in *to); +int +Mono_Posix_ToSockaddrIn (struct sockaddr_in *from, struct Mono_Posix_SockaddrIn* to); + + +struct Mono_Posix_SockaddrIn6 { + int type; + int _sa_family; + unsigned short sin6_port; + unsigned int sin6_flowinfo; + struct Mono_Posix_In6Addr sin6_addr; + unsigned int sin6_scope_id; +}; + +int +Mono_Posix_FromSockaddrIn6 (struct Mono_Posix_SockaddrIn6* from, struct sockaddr_in6 *to); +int +Mono_Posix_ToSockaddrIn6 (struct sockaddr_in6 *from, struct Mono_Posix_SockaddrIn6* to); + + struct Mono_Posix_Stat { guint64 st_dev; /* dev_t */ guint64 st_ino; /* ino_t */ @@ -2013,6 +2079,18 @@ int Mono_Posix_ToUtimbuf (struct utimbuf *from, struct Mono_Posix_Utimbuf* to); +struct Mono_Posix__SockaddrDynamic { + int type; + int sa_family; + unsigned char* data; + gint64 len; +}; + +struct Mono_Posix__SockaddrHeader { + int type; + int sa_family; +}; + struct Mono_Unix_UnixSignal_SignalInfo { int signum; int count; @@ -2035,14 +2113,17 @@ int map_Mono_Posix_AccessMode (int mode); int map_Mono_Posix_FileMode (int mode); int map_Mono_Posix_OpenFlags (int flags); int map_Mono_Posix_WaitOptions (int wait_options); +int Mono_Posix_FromIn6Addr (struct Mono_Posix_In6Addr* source, void* destination); +int Mono_Posix_FromInAddr (struct Mono_Posix_InAddr* source, void* destination); int Mono_Posix_FromRealTimeSignum (int offset, int* rval); +int Mono_Posix_FromSockaddr (struct Mono_Posix__SockaddrHeader* source, void* destination); int Mono_Posix_FromStat (struct Mono_Posix_Stat* source, void* destination); int Mono_Posix_FromStatvfs (struct Mono_Posix_Statvfs* source, void* destination); int Mono_Posix_SIGRTMAX (void); int Mono_Posix_SIGRTMIN (void); -int Mono_Posix_Stdlib__IOFBF (void); -int Mono_Posix_Stdlib__IOLBF (void); -int Mono_Posix_Stdlib__IONBF (void); +int Mono_Posix_SockaddrStorage_get_size (void); +int Mono_Posix_SockaddrUn_get_sizeof_sun_path (void); +int Mono_Posix_Sockaddr_GetNativeSize (struct Mono_Posix__SockaddrHeader* address, gint64* size); int Mono_Posix_Stdlib_BUFSIZ (void); void* Mono_Posix_Stdlib_calloc (guint64 nmemb, guint64 size); int Mono_Posix_Stdlib_clearerr (void* stream); @@ -2078,8 +2159,15 @@ void* Mono_Posix_Stdlib_stdin (void); void* Mono_Posix_Stdlib_stdout (void); guint64 Mono_Posix_Stdlib_strlen (void* s); int Mono_Posix_Stdlib_TMP_MAX (void); +int Mono_Posix_Stdlib__IOFBF (void); +int Mono_Posix_Stdlib__IOLBF (void); +int Mono_Posix_Stdlib__IONBF (void); +int Mono_Posix_Syscall_accept (int socket, struct Mono_Posix__SockaddrHeader* address); +int Mono_Posix_Syscall_accept4 (int socket, struct Mono_Posix__SockaddrHeader* address, int flags); +int Mono_Posix_Syscall_bind (int socket, struct Mono_Posix__SockaddrHeader* address); int Mono_Posix_Syscall_closelog (void); guint64 Mono_Posix_Syscall_confstr (int name, char* buf, guint64 len); +int Mono_Posix_Syscall_connect (int socket, struct Mono_Posix__SockaddrHeader* address); int Mono_Posix_Syscall_creat (const char* pathname, unsigned int mode); int Mono_Posix_Syscall_endfsent (void); int Mono_Posix_Syscall_endgrent (void); @@ -2103,9 +2191,6 @@ int Mono_Posix_Syscall_fstatvfs (int fd, struct Mono_Posix_Statvfs* buf); int Mono_Posix_Syscall_ftruncate (int fd, gint64 length); int Mono_Posix_Syscall_futimens (int fd, struct Mono_Posix_Timespec* times); int Mono_Posix_Syscall_futimes (int fd, struct Mono_Posix_Timeval* tvp); -int Mono_Posix_Syscall_get_at_fdcwd (void); -gint64 Mono_Posix_Syscall_get_utime_now (void); -gint64 Mono_Posix_Syscall_get_utime_omit (void); void* Mono_Posix_Syscall_getcwd (char* buf, guint64 size); int Mono_Posix_Syscall_getdomainname (char* name, guint64 len); int Mono_Posix_Syscall_getfsent (struct Mono_Posix_Syscall__Fstab* fs); @@ -2119,18 +2204,21 @@ int Mono_Posix_Syscall_getgrnam_r (const char* name, struct Mono_Posix_Syscall__ gint64 Mono_Posix_Syscall_gethostid (void); int Mono_Posix_Syscall_gethostname (char* name, guint64 len); int Mono_Posix_Syscall_getlogin_r (char* name, guint64 bufsize); +int Mono_Posix_Syscall_getpeername (int socket, struct Mono_Posix__SockaddrHeader* address); int Mono_Posix_Syscall_getpwent (struct Mono_Posix_Syscall__Passwd* pwbuf); int Mono_Posix_Syscall_getpwnam (const char* name, struct Mono_Posix_Syscall__Passwd* passwd); int Mono_Posix_Syscall_getpwnam_r (const char* name, struct Mono_Posix_Syscall__Passwd* pwbuf, void** pwbufp); int Mono_Posix_Syscall_getpwuid (unsigned int uid, struct Mono_Posix_Syscall__Passwd* passwd); int Mono_Posix_Syscall_getpwuid_r (unsigned int uid, struct Mono_Posix_Syscall__Passwd* pwbuf, void** pwbufp); +int Mono_Posix_Syscall_getsockname (int socket, struct Mono_Posix__SockaddrHeader* address); int Mono_Posix_Syscall_getsockopt (int socket, int level, int option_name, void* option_value, gint64* option_len); int Mono_Posix_Syscall_getsockopt_linger (int socket, int level, int option_name, struct Mono_Posix_Linger* option_value); int Mono_Posix_Syscall_getsockopt_timeval (int socket, int level, int option_name, struct Mono_Posix_Timeval* option_value); int Mono_Posix_Syscall_gettimeofday (struct Mono_Posix_Timeval* tv, void* ignore); gint64 Mono_Posix_Syscall_getxattr (const char* path, const char* name, unsigned char* value, guint64 size); -int Mono_Posix_Syscall_L_ctermid (void); -int Mono_Posix_Syscall_L_cuserid (void); +int Mono_Posix_Syscall_get_at_fdcwd (void); +gint64 Mono_Posix_Syscall_get_utime_now (void); +gint64 Mono_Posix_Syscall_get_utime_omit (void); gint64 Mono_Posix_Syscall_lgetxattr (const char* path, const char* name, unsigned char* value, guint64 size); gint64 Mono_Posix_Syscall_listxattr (const char* path, unsigned char* list, guint64 size); gint64 Mono_Posix_Syscall_llistxattr (const char* path, unsigned char* list, guint64 size); @@ -2140,6 +2228,8 @@ gint64 Mono_Posix_Syscall_lseek (int fd, gint64 offset, int whence); int Mono_Posix_Syscall_lsetxattr (const char* path, const char* name, unsigned char* value, guint64 size, int flags); int Mono_Posix_Syscall_lstat (const char* file_name, struct Mono_Posix_Stat* buf); int Mono_Posix_Syscall_lutimes (const char* filename, struct Mono_Posix_Timeval* tvp); +int Mono_Posix_Syscall_L_ctermid (void); +int Mono_Posix_Syscall_L_cuserid (void); int Mono_Posix_Syscall_mincore (void* start, guint64 length, unsigned char* vec); int Mono_Posix_Syscall_mknod (const char* pathname, unsigned int mode, guint64 dev); int Mono_Posix_Syscall_mknodat (int dirfd, const char* pathname, unsigned int mode, guint64 dev); @@ -2152,8 +2242,8 @@ int Mono_Posix_Syscall_munlock (void* start, guint64 len); int Mono_Posix_Syscall_munmap (void* start, guint64 length); int Mono_Posix_Syscall_nanosleep (struct Mono_Posix_Timespec* req, struct Mono_Posix_Timespec* rem); int Mono_Posix_Syscall_open (const char* pathname, int flags); -int Mono_Posix_Syscall_open_mode (const char* pathname, int flags, unsigned int mode); int Mono_Posix_Syscall_openlog (void* ident, int option, int facility); +int Mono_Posix_Syscall_open_mode (const char* pathname, int flags, unsigned int mode); gint64 Mono_Posix_Syscall_pathconf (const char* path, int name, int defaultError); int Mono_Posix_Syscall_pipe (int* reading, int* writing); int Mono_Posix_Syscall_posix_fadvise (int fd, gint64 offset, gint64 len, int advice); @@ -2171,12 +2261,14 @@ gint64 Mono_Posix_Syscall_readlink (const char* path, unsigned char* buf, guint6 gint64 Mono_Posix_Syscall_readlinkat (int dirfd, const char* pathname, unsigned char* buf, guint64 bufsiz); gint64 Mono_Posix_Syscall_readv (int fd, struct Mono_Posix_Iovec* iov, int iovcnt); gint64 Mono_Posix_Syscall_recv (int socket, void* buffer, guint64 length, int flags); +gint64 Mono_Posix_Syscall_recvfrom (int socket, void* buffer, guint64 length, int flags, struct Mono_Posix__SockaddrHeader* address); int Mono_Posix_Syscall_remap_file_pages (void* start, guint64 size, int prot, gint64 pgoff, int flags); int Mono_Posix_Syscall_removexattr (const char* path, const char* name); int Mono_Posix_Syscall_rewinddir (void* dir); int Mono_Posix_Syscall_seekdir (void* dir, gint64 offset); gint64 Mono_Posix_Syscall_send (int socket, void* message, guint64 length, int flags); gint64 Mono_Posix_Syscall_sendfile (int out_fd, int in_fd, gint64* offset, guint64 count); +gint64 Mono_Posix_Syscall_sendto (int socket, void* message, guint64 length, int flags, struct Mono_Posix__SockaddrHeader* address); int Mono_Posix_Syscall_setdomainname (const char* name, guint64 len); int Mono_Posix_Syscall_setfsent (void); int Mono_Posix_Syscall_setgrent (void); @@ -2215,6 +2307,9 @@ gint64 Mono_Posix_Syscall_write (int fd, void* buf, guint64 count); gint64 Mono_Posix_Syscall_writev (int fd, struct Mono_Posix_Iovec* iov, int iovcnt); int Mono_Posix_Syscall_WSTOPSIG (int status); int Mono_Posix_Syscall_WTERMSIG (int status); +int Mono_Posix_ToIn6Addr (void* source, struct Mono_Posix_In6Addr* destination); +int Mono_Posix_ToInAddr (void* source, struct Mono_Posix_InAddr* destination); +int Mono_Posix_ToSockaddr (void* source, gint64 size, struct Mono_Posix__SockaddrHeader* destination); int Mono_Posix_ToStat (void* source, struct Mono_Posix_Stat* destination); int Mono_Posix_ToStatvfs (void* source, struct Mono_Posix_Statvfs* destination); void* Mono_Unix_UnixSignal_install (int signum); diff --git a/support/sys-socket.c b/support/sys-socket.c index 188df56a18e25..5bfdabf46f15b 100644 --- a/support/sys-socket.c +++ b/support/sys-socket.c @@ -11,6 +11,7 @@ #include #include #include +#include #include @@ -19,6 +20,48 @@ G_BEGIN_DECLS +int +Mono_Posix_SockaddrStorage_get_size (void) +{ + return sizeof (struct sockaddr_storage); +} + +int +Mono_Posix_SockaddrUn_get_sizeof_sun_path (void) +{ + struct sockaddr_un sun; + return sizeof (sun.sun_path); +} + +int +Mono_Posix_FromInAddr (struct Mono_Posix_InAddr* source, void* destination) +{ + memcpy (&((struct in_addr*)destination)->s_addr, &source->s_addr, 4); + return 0; +} + +int +Mono_Posix_ToInAddr (void* source, struct Mono_Posix_InAddr* destination) +{ + memcpy (&destination->s_addr, &((struct in_addr*)source)->s_addr, 4); + return 0; +} + +int +Mono_Posix_FromIn6Addr (struct Mono_Posix_In6Addr* source, void* destination) +{ + memcpy (&((struct in6_addr*)destination)->s6_addr, &source->addr0, 16); + return 0; +} + +int +Mono_Posix_ToIn6Addr (void* source, struct Mono_Posix_In6Addr* destination) +{ + memcpy (&destination->addr0, &((struct in6_addr*)source)->s6_addr, 16); + return 0; +} + + int Mono_Posix_Syscall_socketpair (int domain, int type, int protocol, int* socket1, int* socket2) { @@ -123,6 +166,290 @@ Mono_Posix_Syscall_setsockopt_linger (int socket, int level, int option_name, st return setsockopt (socket, level, option_name, &ling, sizeof (struct linger)); } +static int +get_addrlen (struct Mono_Posix__SockaddrHeader* address, socklen_t* addrlen) +{ + if (!address) { + *addrlen = 0; + return 0; + } + + switch (address->type) { + case Mono_Posix_SockaddrType_SockaddrStorage: + mph_return_if_socklen_t_overflow (((struct Mono_Posix__SockaddrDynamic*) address)->len); + *addrlen = ((struct Mono_Posix__SockaddrDynamic*) address)->len; + return 0; + case Mono_Posix_SockaddrType_SockaddrUn: + mph_return_if_socklen_t_overflow (offsetof (struct sockaddr_un, sun_path) + ((struct Mono_Posix__SockaddrDynamic*) address)->len); + *addrlen = offsetof (struct sockaddr_un, sun_path) + ((struct Mono_Posix__SockaddrDynamic*) address)->len; + return 0; + case Mono_Posix_SockaddrType_Sockaddr: *addrlen = sizeof (struct sockaddr); return 0; + case Mono_Posix_SockaddrType_SockaddrIn: *addrlen = sizeof (struct sockaddr_in); return 0; + case Mono_Posix_SockaddrType_SockaddrIn6: *addrlen = sizeof (struct sockaddr_in6); return 0; + default: + *addrlen = 0; + errno = EINVAL; + return -1; + } +} + +int +Mono_Posix_Sockaddr_GetNativeSize (struct Mono_Posix__SockaddrHeader* address, gint64* size) +{ + socklen_t value; + int r; + + r = get_addrlen (address, &value); + *size = value; + return r; +} + +int +Mono_Posix_FromSockaddr (struct Mono_Posix__SockaddrHeader* source, void* destination) +{ + if (!source) + return 0; + + switch (source->type) { + case Mono_Posix_SockaddrType_SockaddrStorage: + // Do nothing, don't copy source->sa_family into addr->sa_family + return 0; + + case Mono_Posix_SockaddrType_SockaddrUn: + memcpy (((struct sockaddr_un*) destination)->sun_path, ((struct Mono_Posix__SockaddrDynamic*) source)->data, ((struct Mono_Posix__SockaddrDynamic*) source)->len); + break; + + case Mono_Posix_SockaddrType_Sockaddr: + break; + + case Mono_Posix_SockaddrType_SockaddrIn: + if (Mono_Posix_FromSockaddrIn ((struct Mono_Posix_SockaddrIn*) source, (struct sockaddr_in*) destination) != 0) + return -1; + break; + + case Mono_Posix_SockaddrType_SockaddrIn6: + if (Mono_Posix_FromSockaddrIn6 ((struct Mono_Posix_SockaddrIn6*) source, (struct sockaddr_in6*) destination) != 0) + return -1; + break; + + default: + errno = EINVAL; + return -1; + } + + int family; + if (Mono_Posix_FromUnixAddressFamily (source->sa_family, &family) != 0) + return -1; + ((struct sockaddr*) destination)->sa_family = family; + + return 0; +} + +int +Mono_Posix_ToSockaddr (void* source, gint64 size, struct Mono_Posix__SockaddrHeader* destination) +{ + struct Mono_Posix__SockaddrDynamic* destination_dyn; + + if (!destination) + return 0; + + switch (destination->type) { + case Mono_Posix_SockaddrType_Sockaddr: + if (size < offsetof (struct sockaddr, sa_family) + sizeof (sa_family_t)) { + errno = ENOBUFS; + return -1; + } + break; + + case Mono_Posix_SockaddrType_SockaddrStorage: + destination_dyn = ((struct Mono_Posix__SockaddrDynamic*) destination); + if (size > destination_dyn->len) { + errno = ENOBUFS; + return -1; + } + destination_dyn->len = size; + break; + + case Mono_Posix_SockaddrType_SockaddrUn: + destination_dyn = ((struct Mono_Posix__SockaddrDynamic*) destination); + if (size - offsetof (struct sockaddr_un, sun_path) > destination_dyn->len) { + errno = ENOBUFS; + return -1; + } + destination_dyn->len = size - offsetof (struct sockaddr_un, sun_path); + memcpy (destination_dyn->data, ((struct sockaddr_un*) source)->sun_path, size); + break; + + case Mono_Posix_SockaddrType_SockaddrIn: + if (size != sizeof (struct sockaddr_in)) { + errno = ENOBUFS; + return -1; + } + if (Mono_Posix_ToSockaddrIn ((struct sockaddr_in*) source, (struct Mono_Posix_SockaddrIn*) destination) != 0) + return -1; + break; + + case Mono_Posix_SockaddrType_SockaddrIn6: + if (size != sizeof (struct sockaddr_in6)) { + errno = ENOBUFS; + return -1; + } + if (Mono_Posix_ToSockaddrIn6 ((struct sockaddr_in6*) source, (struct Mono_Posix_SockaddrIn6*) destination) != 0) + return -1; + break; + + default: + errno = EINVAL; + return -1; + } + + if (Mono_Posix_ToUnixAddressFamily (((struct sockaddr*) source)->sa_family, &destination->sa_family) != 0) + destination->sa_family = Mono_Posix_UnixAddressFamily_Unknown; + + return 0; +} + +// Macro for allocating space for the native sockaddr_* structure +// Must be a macro because it is using alloca() + +#define ALLOC_SOCKADDR \ + socklen_t addrlen; \ + struct sockaddr* addr; \ + gboolean need_free = 0; \ + \ + if (get_addrlen (address, &addrlen) != 0) \ + return -1; \ + if (address == NULL) { \ + addr = NULL; \ + } else if (address->type == Mono_Posix_SockaddrType_SockaddrStorage) { \ + addr = (struct sockaddr*) ((struct Mono_Posix__SockaddrDynamic*) address)->data; \ + } else if (address->type == Mono_Posix_SockaddrType_SockaddrUn) { \ + /* Use alloca() for up to 2048 bytes, use malloc() otherwise */ \ + need_free = addrlen > 2048; \ + addr = need_free ? malloc (addrlen) : alloca (addrlen); \ + if (!addr) \ + return -1; \ + } else { \ + addr = alloca (addrlen); \ + } + + +int +Mono_Posix_Syscall_bind (int socket, struct Mono_Posix__SockaddrHeader* address) +{ + int r; + + ALLOC_SOCKADDR + if (Mono_Posix_FromSockaddr (address, addr) != 0) { + if (need_free) + free (addr); + return -1; + } + + r = bind (socket, addr, addrlen); + + if (need_free) + free (addr); + + return r; +} + +int +Mono_Posix_Syscall_connect (int socket, struct Mono_Posix__SockaddrHeader* address) +{ + int r; + + ALLOC_SOCKADDR + if (Mono_Posix_FromSockaddr (address, addr) != 0) { + if (need_free) + free (addr); + return -1; + } + + r = connect (socket, addr, addrlen); + + if (need_free) + free (addr); + + return r; +} + +int +Mono_Posix_Syscall_accept (int socket, struct Mono_Posix__SockaddrHeader* address) +{ + int r; + + ALLOC_SOCKADDR + + r = accept (socket, addr, &addrlen); + + if (r != -1 && Mono_Posix_ToSockaddr (addr, addrlen, address) != 0) { + close (r); + r = -1; + } + + if (need_free) + free (addr); + + return r; +} + +int +Mono_Posix_Syscall_accept4 (int socket, struct Mono_Posix__SockaddrHeader* address, int flags) +{ + int r; + + ALLOC_SOCKADDR + + r = accept4 (socket, addr, &addrlen, flags); + + if (r != -1 && Mono_Posix_ToSockaddr (addr, addrlen, address) != 0) { + close (r); + r = -1; + } + + if (need_free) + free (addr); + + return r; +} + +int +Mono_Posix_Syscall_getpeername (int socket, struct Mono_Posix__SockaddrHeader* address) +{ + int r; + + ALLOC_SOCKADDR + + r = getpeername (socket, addr, &addrlen); + + if (r != -1 && Mono_Posix_ToSockaddr (addr, addrlen, address) != 0) + r = -1; + + if (need_free) + free (addr); + + return r; +} + +int +Mono_Posix_Syscall_getsockname (int socket, struct Mono_Posix__SockaddrHeader* address) +{ + int r; + + ALLOC_SOCKADDR + + r = getsockname (socket, addr, &addrlen); + + if (r != -1 && Mono_Posix_ToSockaddr (addr, addrlen, address) != 0) + r = -1; + + if (need_free) + free (addr); + + return r; +} + gint64 Mono_Posix_Syscall_recv (int socket, void* message, guint64 length, int flags) { @@ -138,3 +465,52 @@ Mono_Posix_Syscall_send (int socket, void* message, guint64 length, int flags) return send (socket, message, length, flags); } + +gint64 +Mono_Posix_Syscall_recvfrom (int socket, void* buffer, guint64 length, int flags, struct Mono_Posix__SockaddrHeader* address) +{ + int r; + + mph_return_if_size_t_overflow (length); + + ALLOC_SOCKADDR + + r = recvfrom (socket, buffer, length, flags, addr, &addrlen); + + if (r != -1 && Mono_Posix_ToSockaddr (addr, addrlen, address) != 0) + r = -1; + + if (need_free) + free (addr); + + return r; +} + +gint64 +Mono_Posix_Syscall_sendto (int socket, void* message, guint64 length, int flags, struct Mono_Posix__SockaddrHeader* address) +{ + int r; + + mph_return_if_size_t_overflow (length); + + ALLOC_SOCKADDR + if (Mono_Posix_FromSockaddr (address, addr) != 0) { + if (need_free) + free (addr); + return -1; + } + + r = sendto (socket, message, length, flags, addr, addrlen); + + if (need_free) + free (addr); + + return r; +} + +// vim: noexpandtab +// Local Variables: +// tab-width: 4 +// c-basic-offset: 4 +// indent-tabs-mode: t +// End: