From 2225b6a260e1f0d469e37ec0df68a89e832688cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Steffen=20Kie=C3=9F?= Date: Tue, 19 Jan 2016 20:43:07 +0100 Subject: [PATCH] [Mono.Posix] Add support for sending and receiving socket control messages Add wrappers for struct cmsghdr, the macros for manipulating control messages (CMSG_*) and recvmsg() and sendmsg(). --- configure.ac | 2 + .../NativeConvert.generated.cs | 48 +++ .../Mono.Posix/Mono.Unix.Native/Syscall.cs | 224 +++++++++++++- .../Test/Mono.Unix.Native/SocketTest.cs | 286 ++++++++++++++++++ support/Makefile.am | 1 + support/map.c | 78 +++++ support/map.h | 43 ++- support/stdlib.c | 2 +- support/sys-socket.c | 156 +++++++++- support/sys-uio.c | 14 +- support/sys-uio.h | 18 ++ 11 files changed, 858 insertions(+), 14 deletions(-) create mode 100644 support/sys-uio.h diff --git a/configure.ac b/configure.ac index 82421f3593ade..4131664f25fcb 100644 --- a/configure.ac +++ b/configure.ac @@ -2090,6 +2090,8 @@ if test x$host_win32 = xno; then #include ]) AC_CHECK_TYPES([suseconds_t], [AC_DEFINE(HAVE_SUSECONDS_T)], , [#include ]) + AC_CHECK_TYPES([struct cmsghdr], [AC_DEFINE(HAVE_STRUCT_CMSGHDR)], , + [#include ]) AC_CHECK_TYPES([struct flock], [AC_DEFINE(HAVE_STRUCT_FLOCK)], , [#include #include ]) 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 436235334a157..d980bbf22c600 100644 --- a/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs +++ b/mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs @@ -86,6 +86,22 @@ public static AtFlags ToAtFlags (Int32 value) return rval; } + [DllImport (LIB, EntryPoint="Mono_Posix_FromCmsghdr")] + private static extern int FromCmsghdr (ref Cmsghdr source, IntPtr destination); + + public static bool TryCopy (ref Cmsghdr source, IntPtr destination) + { + return FromCmsghdr (ref source, destination) == 0; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_ToCmsghdr")] + private static extern int ToCmsghdr (IntPtr source, out Cmsghdr destination); + + public static bool TryCopy (IntPtr source, out Cmsghdr destination) + { + return ToCmsghdr (source, out destination) == 0; + } + [DllImport (LIB, EntryPoint="Mono_Posix_FromConfstrName")] private static extern int FromConfstrName (ConfstrName value, out Int32 rval); @@ -1190,6 +1206,38 @@ public static UnixAddressFamily ToUnixAddressFamily (Int32 value) return rval; } + [DllImport (LIB, EntryPoint="Mono_Posix_FromUnixSocketControlMessage")] + private static extern int FromUnixSocketControlMessage (UnixSocketControlMessage value, out Int32 rval); + + public static bool TryFromUnixSocketControlMessage (UnixSocketControlMessage value, out Int32 rval) + { + return FromUnixSocketControlMessage (value, out rval) == 0; + } + + public static Int32 FromUnixSocketControlMessage (UnixSocketControlMessage value) + { + Int32 rval; + if (FromUnixSocketControlMessage (value, out rval) == -1) + ThrowArgumentException (value); + return rval; + } + + [DllImport (LIB, EntryPoint="Mono_Posix_ToUnixSocketControlMessage")] + private static extern int ToUnixSocketControlMessage (Int32 value, out UnixSocketControlMessage rval); + + public static bool TryToUnixSocketControlMessage (Int32 value, out UnixSocketControlMessage rval) + { + return ToUnixSocketControlMessage (value, out rval) == 0; + } + + public static UnixSocketControlMessage ToUnixSocketControlMessage (Int32 value) + { + UnixSocketControlMessage rval; + if (ToUnixSocketControlMessage (value, out rval) == -1) + ThrowArgumentException (value); + return rval; + } + [DllImport (LIB, EntryPoint="Mono_Posix_FromUnixSocketFlags")] private static extern int FromUnixSocketFlags (UnixSocketFlags 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 4ab7ed53ad661..cbeb54b0ec277 100644 --- a/mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs +++ b/mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs @@ -924,6 +924,13 @@ enum SockaddrType : int { MustBeWrapped = 0x8000, } + [Map] + [CLSCompliant (false)] + public enum UnixSocketControlMessage : int { + SCM_RIGHTS = 0x01, /* Transfer file descriptors. */ + SCM_CREDENTIALS = 0x02, /* Credentials passing. */ + } + #endregion #region Structures @@ -1576,6 +1583,60 @@ public bool Equals (In6Addr value) } } + [Map ("struct cmsghdr")] + [CLSCompliant (false)] + public struct Cmsghdr { + public long cmsg_len; + public UnixSocketProtocol cmsg_level; + public UnixSocketControlMessage cmsg_type; + + [DllImport (Syscall.MPH, SetLastError=true, + EntryPoint="Mono_Posix_Cmsghdr_getsize")] + static extern int getsize (); + static readonly int size = getsize (); + public static int Size { + get { + return size; + } + } + + // Read a struct cmsghdr from msgh.msg_control at offset cmsg and convert it to managed Cmsghdr structure + public static unsafe Cmsghdr ReadFromBuffer (Msghdr msgh, long cmsg) + { + if (msgh == null) + throw new ArgumentNullException ("msgh"); + if (msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length) + throw new ArgumentException ("msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length", "msgh"); + if (cmsg < 0 || cmsg + Cmsghdr.Size > msgh.msg_controllen) + throw new ArgumentException ("cmsg offset pointing out of buffer", "cmsg"); + + Cmsghdr hdr; + fixed (byte* ptr = msgh.msg_control) + if (!NativeConvert.TryCopy ((IntPtr) (ptr + cmsg), out hdr)) + throw new ArgumentException ("Failed to convert from native struct", "buffer"); + // SOL_SOCKET has the same value as IPPROTO_ICMP on linux. + // Make sure that cmsg_level is set to SOL_SOCKET in this case. + if (NativeConvert.FromUnixSocketProtocol (hdr.cmsg_level) == NativeConvert.FromUnixSocketProtocol (UnixSocketProtocol.SOL_SOCKET)) + hdr.cmsg_level = UnixSocketProtocol.SOL_SOCKET; + return hdr; + } + + // Convert the Cmsghdr to a native struct cmsghdr and write it to msgh.msg_control at offset cmsg + public unsafe void WriteToBuffer (Msghdr msgh, long cmsg) + { + if (msgh == null) + throw new ArgumentNullException ("msgh"); + if (msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length) + throw new ArgumentException ("msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length", "msgh"); + if (cmsg < 0 || cmsg + Cmsghdr.Size > msgh.msg_controllen) + throw new ArgumentException ("cmsg offset pointing out of buffer", "cmsg"); + + fixed (byte* ptr = msgh.msg_control) + if (!NativeConvert.TryCopy (ref this, (IntPtr) (ptr + cmsg))) + throw new ArgumentException ("Failed to convert to native struct", "buffer"); + } + } + #endregion #region Classes @@ -2386,6 +2447,18 @@ public bool Equals (SockaddrIn6 value) } } + [CLSCompliant (false)] + public sealed class Msghdr + { + public Sockaddr msg_name; + // msg_name_len is part of the Sockaddr structure + public Iovec[] msg_iov; + public int msg_iovlen; + public byte[] msg_control; + public long msg_controllen; + public MessageFlags msg_flags; + } + // // 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. @@ -5250,7 +5323,7 @@ public static long pwritev (int fd, Iovec[] iov, long offset) #region Declarations // - // + // -- COMPLETE // // socket(2) @@ -5642,6 +5715,155 @@ public static unsafe long sendto (int socket, byte[] message, ulong length, Mess return sendto (socket, ptr, length, flags, address); } + // structure for recvmsg() and sendmsg() + unsafe struct _Msghdr + { + public Iovec* msg_iov; + public int msg_iovlen; + public byte* msg_control; + public long msg_controllen; + public int msg_flags; + + public _Msghdr (Msghdr message, Iovec* ptr_msg_iov, byte* ptr_msg_control) + { + if (message.msg_iovlen > message.msg_iov.Length || message.msg_iovlen < 0) + throw new ArgumentException ("message.msg_iovlen > message.msg_iov.Length || message.msg_iovlen < 0", "message"); + msg_iov = ptr_msg_iov; + msg_iovlen = message.msg_iovlen; + + if (message.msg_control == null && message.msg_controllen != 0) + throw new ArgumentException ("message.msg_control == null && message.msg_controllen != 0", "message"); + if (message.msg_control != null && message.msg_controllen > message.msg_control.Length) + throw new ArgumentException ("message.msg_controllen > message.msg_control.Length", "message"); + msg_control = ptr_msg_control; + msg_controllen = message.msg_controllen; + + msg_flags = 0; // msg_flags is only passed out of the kernel + } + + public void Update (Msghdr message) + { + message.msg_controllen = msg_controllen; + message.msg_flags = NativeConvert.ToMessageFlags (msg_flags); + } + } + + // recvmsg(2) + // ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_recvmsg")] + static extern unsafe long sys_recvmsg (int socket, ref _Msghdr message, _SockaddrHeader* msg_name, int flags); + + public static unsafe long recvmsg (int socket, Msghdr message, MessageFlags flags) + { + var _flags = NativeConvert.FromMessageFlags (flags); + var address = message.msg_name; + fixed (byte* ptr_msg_control = message.msg_control) + fixed (Iovec* ptr_msg_iov = message.msg_iov) { + var _message = new _Msghdr (message, ptr_msg_iov, ptr_msg_control); + long r; + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: true); + r = sys_recvmsg (socket, ref _message, Sockaddr.GetNative (&dyn, addr), _flags); + dyn.Update (address); + } + _message.Update (message); + return r; + } + } + + // sendmsg(2) + // ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags); + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_sendmsg")] + static extern unsafe long sys_sendmsg (int socket, ref _Msghdr message, _SockaddrHeader* msg_name, int flags); + + public static unsafe long sendmsg (int socket, Msghdr message, MessageFlags flags) + { + var _flags = NativeConvert.FromMessageFlags (flags); + var address = message.msg_name; + fixed (byte* ptr_msg_control = message.msg_control) + fixed (Iovec* ptr_msg_iov = message.msg_iov) { + var _message = new _Msghdr (message, ptr_msg_iov, ptr_msg_control); + fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type) + fixed (byte* data = Sockaddr.GetDynamicData (address)) { + var dyn = new _SockaddrDynamic (address, data, useMaxLength: false); + return sys_sendmsg (socket, ref _message, Sockaddr.GetNative (&dyn, addr), _flags); + } + } + } + + // cmsg(3) + // struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh); + // struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg); + // size_t CMSG_ALIGN(size_t length); + // size_t CMSG_SPACE(size_t length); + // size_t CMSG_LEN(size_t length); + // unsigned char *CMSG_DATA(struct cmsghdr *cmsg); + + // Wrapper methods use long offsets into msg_control instead of a + // struct cmsghdr *cmsg pointer because pointers into a byte[] aren't + // stable when the array is not pinned. + // NULL is mapped to -1. + + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_CMSG_FIRSTHDR")] + static extern unsafe long CMSG_FIRSTHDR (byte* msg_control, long msg_controllen); + + public static unsafe long CMSG_FIRSTHDR (Msghdr msgh) + { + if (msgh.msg_control == null && msgh.msg_controllen != 0) + throw new ArgumentException ("msgh.msg_control == null && msgh.msg_controllen != 0", "msgh"); + if (msgh.msg_control != null && msgh.msg_controllen > msgh.msg_control.Length) + throw new ArgumentException ("msgh.msg_controllen > msgh.msg_control.Length", "msgh"); + + fixed (byte* ptr = msgh.msg_control) + return CMSG_FIRSTHDR (ptr, msgh.msg_controllen); + } + + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_CMSG_NXTHDR")] + static extern unsafe long CMSG_NXTHDR (byte* msg_control, long msg_controllen, long cmsg); + + public static unsafe long CMSG_NXTHDR (Msghdr msgh, long cmsg) + { + if (msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length) + throw new ArgumentException ("msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length", "msgh"); + if (cmsg < 0 || cmsg + Cmsghdr.Size > msgh.msg_controllen) + throw new ArgumentException ("cmsg offset pointing out of buffer", "cmsg"); + + fixed (byte* ptr = msgh.msg_control) + return CMSG_NXTHDR (ptr, msgh.msg_controllen, cmsg); + } + + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_CMSG_DATA")] + static extern unsafe long CMSG_DATA (byte* msg_control, long msg_controllen, long cmsg); + + public static unsafe long CMSG_DATA (Msghdr msgh, long cmsg) + { + if (msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length) + throw new ArgumentException ("msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length", "msgh"); + if (cmsg < 0 || cmsg + Cmsghdr.Size > msgh.msg_controllen) + throw new ArgumentException ("cmsg offset pointing out of buffer", "cmsg"); + + fixed (byte* ptr = msgh.msg_control) + return CMSG_DATA (ptr, msgh.msg_controllen, cmsg); + } + + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_CMSG_ALIGN")] + public static extern ulong CMSG_ALIGN (ulong length); + + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_CMSG_SPACE")] + public static extern ulong CMSG_SPACE (ulong length); + + [DllImport (MPH, SetLastError=true, + EntryPoint="Mono_Posix_Syscall_CMSG_LEN")] + public static extern ulong CMSG_LEN (ulong length); + #endregion } 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 dd5ed9bf40b77..c16884dca0c9d 100644 --- a/mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs +++ b/mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs @@ -596,6 +596,292 @@ public void SendToRecvFrom () Assert.AreEqual (buffer1[i], buffer2[i]); }); } + + [Test] + public unsafe void SendMsgRecvMsg () + { + WithSocketPair ((so1, so2) => { + long ret; + var buffer1 = new byte[] { 42, 43, 44 }; + fixed (byte* ptr_buffer1 = buffer1) { + var iovecs1 = new Iovec[] { + new Iovec { + iov_base = (IntPtr) ptr_buffer1, + iov_len = (ulong) buffer1.Length, + }, + }; + var msghdr1 = new Msghdr { + msg_iov = iovecs1, + msg_iovlen = 1, + }; + ret = Syscall.sendmsg (so1, msghdr1, 0); + } + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var buffer2 = new byte[1024]; + fixed (byte* ptr_buffer2 = buffer2) { + var iovecs2 = new Iovec[] { + new Iovec { + iov_base = (IntPtr) ptr_buffer2, + iov_len = (ulong) buffer2.Length, + }, + }; + var msghdr2 = new Msghdr { + msg_iov = iovecs2, + msg_iovlen = 1, + }; + ret = Syscall.recvmsg (so2, msghdr2, 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 unsafe void SendMsgRecvMsgAddress () + { + 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 }; + fixed (byte* ptr_buffer1 = buffer1) { + var iovecs1 = new Iovec[] { + new Iovec { + iov_base = (IntPtr) ptr_buffer1, + iov_len = (ulong) buffer1.Length, + }, + }; + var msghdr1 = new Msghdr { + msg_name = remoteAddress, + msg_iov = iovecs1, + msg_iovlen = 1, + }; + ret = Syscall.sendmsg (so2, msghdr1, 0); + msghdr1.msg_name = remoteAddress.ToSockaddrStorage (); + if (ret >= 0) + ret = Syscall.sendmsg (so2, msghdr1, 0); + } + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var senderAddress = new SockaddrIn (); + var senderAddressStorage = new SockaddrStorage (); + var buffer2 = new byte[1024]; + var buffer3 = new byte[1024]; + fixed (byte* ptr_buffer2 = buffer2, ptr_buffer3 = buffer3) { + var iovecs2 = new Iovec[] { + new Iovec { + iov_base = (IntPtr) ptr_buffer2, + iov_len = (ulong) buffer2.Length, + }, + }; + var msghdr2 = new Msghdr { + msg_name = senderAddress, + msg_iov = iovecs2, + msg_iovlen = 1, + }; + ret = Syscall.recvmsg (so1, msghdr2, 0); + msghdr2.msg_name = senderAddressStorage; + iovecs2[0].iov_base = (IntPtr) ptr_buffer3; + if (ret >= 0) + ret = Syscall.recvmsg (so1, msghdr2, 0); + } + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + Assert.AreEqual (senderAddress.sa_family, UnixAddressFamily.AF_INET); + Assert.AreEqual (senderAddress.sin_addr, new InAddr (127, 0, 0, 1)); + var senderAddress2 = SockaddrIn.FromSockaddrStorage (senderAddressStorage); + Assert.AreEqual (senderAddress2.sa_family, UnixAddressFamily.AF_INET); + Assert.AreEqual (senderAddress2.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]); + for (int i = 0; i < buffer1.Length; i++) + Assert.AreEqual (buffer1[i], buffer3[i]); + }); + } + + [Test] + public unsafe void ControlMsg () + { + // Create two socket pairs and send inner_so1 and inner_so2 over the other socket pair using SCM_RIGHTS + WithSocketPair ((inner_so1, inner_so2) => { + WithSocketPair ((so1, so2) => { + // Create two SCM_RIGHTS control messages + var cmsg = new byte[2 * Syscall.CMSG_SPACE (sizeof (int))]; + var hdr = new Cmsghdr { + cmsg_len = (long) Syscall.CMSG_LEN (sizeof (int)), + cmsg_level = UnixSocketProtocol.SOL_SOCKET, + cmsg_type = UnixSocketControlMessage.SCM_RIGHTS, + }; + var msghdr1 = new Msghdr { + msg_control = cmsg, + msg_controllen = cmsg.Length, + }; + long offset = 0; + hdr.WriteToBuffer (msghdr1, offset); + var dataOffset = Syscall.CMSG_DATA (msghdr1, offset); + fixed (byte* ptr = msghdr1.msg_control) { + ((int*) (ptr + dataOffset))[0] = inner_so1; + } + offset = (long) Syscall.CMSG_SPACE (sizeof (int)); + hdr.WriteToBuffer (msghdr1, offset); + dataOffset = Syscall.CMSG_DATA (msghdr1, offset); + fixed (byte* ptr = msghdr1.msg_control) { + ((int*) (ptr + dataOffset))[0] = inner_so2; + } + + long ret; + var buffer1 = new byte[] { 42, 43, 44 }; + fixed (byte* ptr_buffer1 = buffer1) { + var iovecs1 = new Iovec[] { + new Iovec { + iov_base = (IntPtr) ptr_buffer1, + iov_len = (ulong) buffer1.Length, + }, + }; + msghdr1.msg_iov = iovecs1; + msghdr1.msg_iovlen = 1; + // Send message twice + ret = Syscall.sendmsg (so1, msghdr1, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + ret = Syscall.sendmsg (so1, msghdr1, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + } + + // Receive without control message buffer + var buffer2 = new byte[1024]; + var msghdr2 = new Msghdr { }; + fixed (byte* ptr_buffer2 = buffer2) { + var iovecs2 = new Iovec[] { + new Iovec { + iov_base = (IntPtr) ptr_buffer2, + iov_len = (ulong) buffer2.Length, + }, + }; + msghdr2.msg_iov = iovecs2; + msghdr2.msg_iovlen = 1; + ret = Syscall.recvmsg (so2, msghdr2, 0); + } + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + Assert.IsTrue ((msghdr2.msg_flags & MessageFlags.MSG_CTRUNC) != 0); // Control message has been truncated + + Assert.AreEqual (buffer1.Length, ret); + for (int i = 0; i < buffer1.Length; i++) + Assert.AreEqual (buffer1[i], buffer2[i]); + + // Receive with control message buffer + buffer2 = new byte[1024]; + var cmsg2 = new byte[1024]; + msghdr2 = new Msghdr { + msg_control = cmsg2, + msg_controllen = cmsg2.Length, + }; + fixed (byte* ptr_buffer2 = buffer2) { + var iovecs2 = new Iovec[] { + new Iovec { + iov_base = (IntPtr) ptr_buffer2, + iov_len = (ulong) buffer2.Length, + }, + }; + msghdr2.msg_iov = iovecs2; + msghdr2.msg_iovlen = 1; + ret = Syscall.recvmsg (so2, msghdr2, 0); + } + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var fds = new global::System.Collections.Generic.List (); + for (offset = Syscall.CMSG_FIRSTHDR (msghdr2); offset != -1; offset = Syscall.CMSG_NXTHDR (msghdr2, offset)) { + var recvHdr = Cmsghdr.ReadFromBuffer (msghdr2, offset); + var recvDataOffset = Syscall.CMSG_DATA (msghdr2, offset); + var bytes = recvHdr.cmsg_len - (recvDataOffset - offset); + Assert.AreEqual (bytes % sizeof (int), 0); + var fdCount = bytes / sizeof (int); + fixed (byte* ptr = msghdr2.msg_control) + for (int i = 0; i < fdCount; i++) + fds.Add (((int*) (ptr + recvDataOffset))[i]); + } + try { + Assert.IsTrue ((msghdr2.msg_flags & MessageFlags.MSG_CTRUNC) == 0); // Control message has not been truncated + + Assert.AreEqual (buffer1.Length, ret); + for (int i = 0; i < buffer1.Length; i++) + Assert.AreEqual (buffer1[i], buffer2[i]); + + Assert.AreEqual (fds.Count, 2); + + // Send message over the first received fd and receive it over inner_so2 + var buffer3 = new byte[] { 16, 17 }; + ret = Syscall.send (fds[0], buffer3, (ulong) buffer3.Length, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var buffer4 = new byte[1024]; + ret = Syscall.recv (inner_so2, buffer4, (ulong) buffer4.Length, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + Assert.AreEqual (buffer3.Length, ret); + for (int i = 0; i < buffer3.Length; i++) + Assert.AreEqual (buffer3[i], buffer4[i]); + + // Send message over inner_so1 and receive it second received fd + var buffer5 = new byte[] { 10, 40, 0, 1 }; + ret = Syscall.send (inner_so1, buffer5, (ulong) buffer5.Length, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + var buffer6 = new byte[1024]; + ret = Syscall.recv (fds[1], buffer6, (ulong) buffer6.Length, 0); + if (ret < 0) + UnixMarshal.ThrowExceptionForLastError (); + + Assert.AreEqual (buffer5.Length, ret); + for (int i = 0; i < buffer5.Length; i++) + Assert.AreEqual (buffer5[i], buffer6[i]); + } finally { + foreach (var fd in fds) + if (Syscall.close (fd) < 0) + UnixMarshal.ThrowExceptionForLastError (); + } + }); + }); + } } } diff --git a/support/Makefile.am b/support/Makefile.am index be3864cc9dab1..969ff60e4db00 100644 --- a/support/Makefile.am +++ b/support/Makefile.am @@ -44,6 +44,7 @@ MPH_UNIX_SOURCE = \ sys-statvfs.c \ sys-time.c \ sys-uio.c \ + sys-uio.h \ sys-utsname.c \ sys-wait.c \ sys-xattr.c \ diff --git a/support/map.c b/support/map.c index 6201c260c9336..136f2b7187810 100644 --- a/support/map.c +++ b/support/map.c @@ -367,6 +367,48 @@ int Mono_Posix_ToAtFlags (int x, int *r) return 0; } +#ifdef HAVE_STRUCT_CMSGHDR +int +Mono_Posix_FromCmsghdr (struct Mono_Posix_Cmsghdr *from, struct cmsghdr *to) +{ + _cnm_return_val_if_overflow (gint64, from->cmsg_len, -1); + + memset (to, 0, sizeof(*to)); + + to->cmsg_len = from->cmsg_len; + if (Mono_Posix_FromUnixSocketProtocol (from->cmsg_level, &to->cmsg_level) != 0) { + return -1; + } + if (Mono_Posix_FromUnixSocketControlMessage (from->cmsg_type, &to->cmsg_type) != 0) { + return -1; + } + + return 0; +} +#endif /* ndef HAVE_STRUCT_CMSGHDR */ + + +#ifdef HAVE_STRUCT_CMSGHDR +int +Mono_Posix_ToCmsghdr (struct cmsghdr *from, struct Mono_Posix_Cmsghdr *to) +{ + _cnm_return_val_if_overflow (gint64, from->cmsg_len, -1); + + memset (to, 0, sizeof(*to)); + + to->cmsg_len = from->cmsg_len; + if (Mono_Posix_ToUnixSocketProtocol (from->cmsg_level, &to->cmsg_level) != 0) { + return -1; + } + if (Mono_Posix_ToUnixSocketControlMessage (from->cmsg_type, &to->cmsg_type) != 0) { + return -1; + } + + return 0; +} +#endif /* ndef HAVE_STRUCT_CMSGHDR */ + + int Mono_Posix_FromConfstrName (int x, int *r) { *r = 0; @@ -8247,6 +8289,42 @@ int Mono_Posix_ToUnixAddressFamily (int x, int *r) errno = EINVAL; return -1; } +int Mono_Posix_FromUnixSocketControlMessage (int x, int *r) +{ + *r = 0; + if (x == Mono_Posix_UnixSocketControlMessage_SCM_CREDENTIALS) +#ifdef SCM_CREDENTIALS + {*r = SCM_CREDENTIALS; return 0;} +#else /* def SCM_CREDENTIALS */ + {errno = EINVAL; return -1;} +#endif /* ndef SCM_CREDENTIALS */ + if (x == Mono_Posix_UnixSocketControlMessage_SCM_RIGHTS) +#ifdef SCM_RIGHTS + {*r = SCM_RIGHTS; return 0;} +#else /* def SCM_RIGHTS */ + {errno = EINVAL; return -1;} +#endif /* ndef SCM_RIGHTS */ + if (x == 0) + return 0; + errno = EINVAL; return -1; +} + +int Mono_Posix_ToUnixSocketControlMessage (int x, int *r) +{ + *r = 0; + if (x == 0) + return 0; +#ifdef SCM_CREDENTIALS + if (x == SCM_CREDENTIALS) + {*r = Mono_Posix_UnixSocketControlMessage_SCM_CREDENTIALS; return 0;} +#endif /* ndef SCM_CREDENTIALS */ +#ifdef SCM_RIGHTS + if (x == SCM_RIGHTS) + {*r = Mono_Posix_UnixSocketControlMessage_SCM_RIGHTS; return 0;} +#endif /* ndef SCM_RIGHTS */ + errno = EINVAL; return -1; +} + int Mono_Posix_FromUnixSocketFlags (int x, int *r) { *r = 0; diff --git a/support/map.h b/support/map.h index e95b4f9c4d8bc..ccd221b106a21 100644 --- a/support/map.h +++ b/support/map.h @@ -1612,6 +1612,15 @@ enum Mono_Posix_UnixAddressFamily { int Mono_Posix_FromUnixAddressFamily (int x, int *r); int Mono_Posix_ToUnixAddressFamily (int x, int *r); +enum Mono_Posix_UnixSocketControlMessage { + Mono_Posix_UnixSocketControlMessage_SCM_CREDENTIALS = 0x00000002, + #define Mono_Posix_UnixSocketControlMessage_SCM_CREDENTIALS Mono_Posix_UnixSocketControlMessage_SCM_CREDENTIALS + Mono_Posix_UnixSocketControlMessage_SCM_RIGHTS = 0x00000001, + #define Mono_Posix_UnixSocketControlMessage_SCM_RIGHTS Mono_Posix_UnixSocketControlMessage_SCM_RIGHTS +}; +int Mono_Posix_FromUnixSocketControlMessage (int x, int *r); +int Mono_Posix_ToUnixSocketControlMessage (int x, int *r); + enum Mono_Posix_UnixSocketFlags { Mono_Posix_UnixSocketFlags_SOCK_CLOEXEC = 0x00080000, #define Mono_Posix_UnixSocketFlags_SOCK_CLOEXEC Mono_Posix_UnixSocketFlags_SOCK_CLOEXEC @@ -1819,6 +1828,7 @@ int Mono_Posix_ToXattrFlags (int x, int *r); * Managed Structure Declarations */ +struct Mono_Posix_Cmsghdr; struct Mono_Posix_Flock; struct Mono_Posix_In6Addr; struct Mono_Posix_InAddr; @@ -1832,6 +1842,7 @@ struct Mono_Posix_Statvfs; struct Mono_Posix_Syscall__Dirent; struct Mono_Posix_Syscall__Fstab; struct Mono_Posix_Syscall__Group; +struct Mono_Posix_Syscall__Msghdr; struct Mono_Posix_Syscall__Passwd; struct Mono_Posix_Syscall__Utsname; struct Mono_Posix_Timespec; @@ -1846,6 +1857,7 @@ struct Mono_Unix_UnixSignal_SignalInfo; * Inferred Structure Declarations */ +struct cmsghdr; struct flock; struct iovec; struct linger; @@ -1867,6 +1879,18 @@ typedef int (*Mono_Posix_RuntimeIsShuttingDown) (void); * Structures */ +struct Mono_Posix_Cmsghdr { + gint64 cmsg_len; + int cmsg_level; + int cmsg_type; +}; + +int +Mono_Posix_FromCmsghdr (struct Mono_Posix_Cmsghdr* from, struct cmsghdr *to); +int +Mono_Posix_ToCmsghdr (struct cmsghdr *from, struct Mono_Posix_Cmsghdr* to); + + struct Mono_Posix_Flock { short l_type; short l_whence; @@ -2014,6 +2038,14 @@ struct Mono_Posix_Syscall__Group { void* _gr_buf_; }; +struct Mono_Posix_Syscall__Msghdr { + struct Mono_Posix_Iovec* msg_iov; + int msg_iovlen; + unsigned char* msg_control; + gint64 msg_controllen; + int msg_flags; +}; + struct Mono_Posix_Syscall__Passwd { void* pw_name; void* pw_passwd; @@ -2114,6 +2146,7 @@ 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_Cmsghdr_getsize (void); 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); @@ -2131,7 +2164,6 @@ int Mono_Posix_Stdlib_clearerr (void* stream); void* Mono_Posix_Stdlib_CreateFilePosition (void); int Mono_Posix_Stdlib_DumpFilePosition (char* buf, void* handle, int len); int Mono_Posix_Stdlib_EOF (void); -const char* Mono_Unix_VersionString (void); int Mono_Posix_Stdlib_EXIT_FAILURE (void); int Mono_Posix_Stdlib_EXIT_SUCCESS (void); int Mono_Posix_Stdlib_fgetpos (void* stream, void* pos); @@ -2168,6 +2200,12 @@ int Mono_Posix_Syscall_accept (int socket, struct Mono_Posix__SockaddrHeader* ad 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_CMSG_ALIGN (guint64 length); +gint64 Mono_Posix_Syscall_CMSG_DATA (unsigned char* msg_control, gint64 msg_controllen, gint64 cmsg); +gint64 Mono_Posix_Syscall_CMSG_FIRSTHDR (unsigned char* msg_control, gint64 msg_controllen); +guint64 Mono_Posix_Syscall_CMSG_LEN (guint64 length); +gint64 Mono_Posix_Syscall_CMSG_NXTHDR (unsigned char* msg_control, gint64 msg_controllen, gint64 cmsg); +guint64 Mono_Posix_Syscall_CMSG_SPACE (guint64 length); 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); @@ -2264,12 +2302,14 @@ gint64 Mono_Posix_Syscall_readlinkat (int dirfd, const char* pathname, unsigned 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); +gint64 Mono_Posix_Syscall_recvmsg (int socket, struct Mono_Posix_Syscall__Msghdr* message, struct Mono_Posix__SockaddrHeader* msg_name, int flags); 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_sendmsg (int socket, struct Mono_Posix_Syscall__Msghdr* message, struct Mono_Posix__SockaddrHeader* msg_name, int flags); 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); @@ -2317,6 +2357,7 @@ int Mono_Posix_ToStatvfs (void* source, struct Mono_Posix_Statvfs* destination); void* Mono_Unix_UnixSignal_install (int signum); int Mono_Unix_UnixSignal_uninstall (void* info); int Mono_Unix_UnixSignal_WaitAny (void** infos, int count, int timeout, Mono_Posix_RuntimeIsShuttingDown shutting_down); +void* Mono_Unix_VersionString (void); int wexitstatus (int status); int wifexited (int status); int wifsignaled (int status); diff --git a/support/stdlib.c b/support/stdlib.c index 523ae92482899..0c4358a51402d 100644 --- a/support/stdlib.c +++ b/support/stdlib.c @@ -15,7 +15,7 @@ G_BEGIN_DECLS // See Stdlib.cs -const char * +void* Mono_Unix_VersionString () { return "MonoProject-2015-12-1"; diff --git a/support/sys-socket.c b/support/sys-socket.c index 36da7d51661d1..9d43087ecdd0a 100644 --- a/support/sys-socket.c +++ b/support/sys-socket.c @@ -17,6 +17,7 @@ #include "map.h" #include "mph.h" +#include "sys-uio.h" G_BEGIN_DECLS @@ -33,6 +34,12 @@ Mono_Posix_SockaddrUn_get_sizeof_sun_path (void) return sizeof (sun.sun_path); } +int +Mono_Posix_Cmsghdr_getsize (void) +{ + return sizeof (struct cmsghdr); +} + int Mono_Posix_FromInAddr (struct Mono_Posix_InAddr* source, void* destination) { @@ -394,10 +401,10 @@ Mono_Posix_Syscall_accept (int socket, struct Mono_Posix__SockaddrHeader* addres return r; } +#ifdef HAVE_ACCEPT4 int Mono_Posix_Syscall_accept4 (int socket, struct Mono_Posix__SockaddrHeader* address, int flags) { -#ifdef HAVE_ACCEPT4 int r; ALLOC_SOCKADDR @@ -413,11 +420,8 @@ Mono_Posix_Syscall_accept4 (int socket, struct Mono_Posix__SockaddrHeader* addre free (addr); return r; -#else - errno = EINVAL; - return -1; -#endif } +#endif int Mono_Posix_Syscall_getpeername (int socket, struct Mono_Posix__SockaddrHeader* address) @@ -513,6 +517,148 @@ Mono_Posix_Syscall_sendto (int socket, void* message, guint64 length, int flags, return r; } +gint64 +Mono_Posix_Syscall_recvmsg (int socket, struct Mono_Posix_Syscall__Msghdr* message, struct Mono_Posix__SockaddrHeader* address, int flags) +{ + struct msghdr hdr; + int r; + + ALLOC_SOCKADDR + + memset (&hdr, 0, sizeof (struct msghdr)); + + hdr.msg_name = addr; + hdr.msg_namelen = addrlen; + hdr.msg_iovlen = message->msg_iovlen; + hdr.msg_control = message->msg_control; + hdr.msg_controllen = message->msg_controllen; + + hdr.msg_iov = _mph_from_iovec_array (message->msg_iov, message->msg_iovlen); + + r = recvmsg (socket, &hdr, flags); + + if (r != -1 && Mono_Posix_ToSockaddr (addr, hdr.msg_namelen, address) != 0) + r = -1; + + free (hdr.msg_iov); + if (need_free) + free (addr); + + message->msg_controllen = hdr.msg_controllen; + message->msg_flags = hdr.msg_flags; + + return r; +} + +gint64 +Mono_Posix_Syscall_sendmsg (int socket, struct Mono_Posix_Syscall__Msghdr* message, struct Mono_Posix__SockaddrHeader* address, int flags) +{ + struct msghdr hdr; + int r; + + ALLOC_SOCKADDR + if (Mono_Posix_FromSockaddr (address, addr) != 0) { + if (need_free) + free (addr); + return -1; + } + + memset (&hdr, 0, sizeof (struct msghdr)); + + hdr.msg_name = addr; + hdr.msg_namelen = addrlen; + hdr.msg_iovlen = message->msg_iovlen; + hdr.msg_control = message->msg_control; + hdr.msg_controllen = message->msg_controllen; + + hdr.msg_iov = _mph_from_iovec_array (message->msg_iov, message->msg_iovlen); + + r = sendmsg (socket, &hdr, flags); + + free (hdr.msg_iov); + if (need_free) + free (addr); + + return r; +} + +static inline void make_msghdr (struct msghdr* hdr, unsigned char* msg_control, gint64 msg_controllen) +{ + memset (hdr, 0, sizeof (struct msghdr)); + hdr->msg_control = msg_control; + hdr->msg_controllen = msg_controllen; +} +static inline struct cmsghdr* from_offset (unsigned char* msg_control, gint64 offset) +{ + if (offset == -1) + return NULL; + return (struct cmsghdr*) (msg_control + offset); +} +static inline gint64 to_offset (unsigned char* msg_control, void* hdr) +{ + if (!hdr) + return -1; + return ((unsigned char*) hdr) - msg_control; +} + +#ifdef CMSG_FIRSTHDR +gint64 +Mono_Posix_Syscall_CMSG_FIRSTHDR (unsigned char* msg_control, gint64 msg_controllen) +{ + struct msghdr hdr; + + make_msghdr (&hdr, msg_control, msg_controllen); + return to_offset (msg_control, CMSG_FIRSTHDR (&hdr)); +} +#endif + +#ifdef CMSG_NXTHDR +gint64 +Mono_Posix_Syscall_CMSG_NXTHDR (unsigned char* msg_control, gint64 msg_controllen, gint64 cmsg) +{ + struct msghdr hdr; + + make_msghdr (&hdr, msg_control, msg_controllen); + return to_offset (msg_control, CMSG_NXTHDR (&hdr, from_offset (msg_control, cmsg))); +} +#endif + +#ifdef CMSG_DATA +gint64 +Mono_Posix_Syscall_CMSG_DATA (unsigned char* msg_control, gint64 msg_controllen, gint64 cmsg) +{ + return to_offset (msg_control, CMSG_DATA (from_offset (msg_control, cmsg))); +} +#endif + +#ifdef CMSG_ALIGN +guint64 +Mono_Posix_Syscall_CMSG_ALIGN (guint64 length) +{ + return CMSG_ALIGN (length); +} +#endif + +#ifdef CMSG_SPACE +guint64 +Mono_Posix_Syscall_CMSG_SPACE (guint64 length) +{ + return CMSG_SPACE (length); +} +#endif + +#ifdef CMSG_LEN +guint64 +Mono_Posix_Syscall_CMSG_LEN (guint64 length) +{ + return CMSG_LEN (length); +} +#endif + +/* + * vim: noexpandtab + */ + // vim: noexpandtab // Local Variables: // tab-width: 4 diff --git a/support/sys-uio.c b/support/sys-uio.c index f6b80e0d740ad..53e162f9fdada 100644 --- a/support/sys-uio.c +++ b/support/sys-uio.c @@ -11,6 +11,8 @@ #define _GNU_SOURCE #endif /* ndef _GNU_SOURCE */ +#include "sys-uio.h" + #include #include "map.h" @@ -18,8 +20,8 @@ G_BEGIN_DECLS -static struct iovec* -from_iovec (struct Mono_Posix_Iovec *iov, gint32 iovcnt) +struct iovec* +_mph_from_iovec_array (struct Mono_Posix_Iovec *iov, gint32 iovcnt) { struct iovec* v; gint32 i; @@ -51,7 +53,7 @@ Mono_Posix_Syscall_readv (int dirfd, struct Mono_Posix_Iovec *iov, gint32 iovcnt struct iovec* v; gint64 res; - v = from_iovec (iov, iovcnt); + v = _mph_from_iovec_array (iov, iovcnt); if (!v) { return -1; } @@ -69,7 +71,7 @@ Mono_Posix_Syscall_writev (int dirfd, struct Mono_Posix_Iovec *iov, gint32 iovcn struct iovec* v; gint64 res; - v = from_iovec (iov, iovcnt); + v = _mph_from_iovec_array (iov, iovcnt); if (!v) { return -1; } @@ -89,7 +91,7 @@ Mono_Posix_Syscall_preadv (int dirfd, struct Mono_Posix_Iovec *iov, gint32 iovcn mph_return_if_off_t_overflow (off); - v = from_iovec (iov, iovcnt); + v = _mph_from_iovec_array (iov, iovcnt); if (!v) { return -1; } @@ -109,7 +111,7 @@ Mono_Posix_Syscall_pwritev (int dirfd, struct Mono_Posix_Iovec *iov, gint32 iovc mph_return_if_off_t_overflow (off); - v = from_iovec (iov, iovcnt); + v = _mph_from_iovec_array (iov, iovcnt); if (!v) { return -1; } diff --git a/support/sys-uio.h b/support/sys-uio.h new file mode 100644 index 0000000000000..b5fb7c29248c0 --- /dev/null +++ b/support/sys-uio.h @@ -0,0 +1,18 @@ +#ifndef SYS_UIO_H +#define SYS_UIO_H + +#include + +#include + +#include "map.h" +#include "mph.h" + +G_BEGIN_DECLS + +MPH_INTERNAL struct iovec* +_mph_from_iovec_array (struct Mono_Posix_Iovec *iov, gint32 iovcnt); + +G_END_DECLS + +#endif /* SYS_UIO_H */