diff options
author | Steffen Kieß <s-kiess@web.de> | 2016-01-19 22:43:07 +0300 |
---|---|---|
committer | Steffen Kieß <s-kiess@web.de> | 2016-01-19 22:43:07 +0300 |
commit | 2225b6a260e1f0d469e37ec0df68a89e832688cd (patch) | |
tree | cce4e43c0ac7fbcbfe940222a58faf800a41522b /mcs/class/Mono.Posix | |
parent | 5436bc0e5ce16bcb2359c3ef9202e5e46f6ec115 (diff) |
[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().
Diffstat (limited to 'mcs/class/Mono.Posix')
3 files changed, 557 insertions, 1 deletions
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 436235334a1..d980bbf22c6 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 @@ namespace Mono.Unix.Native { 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 @@ namespace Mono.Unix.Native { 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 4ab7ed53ad6..cbeb54b0ec2 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 @@ namespace Mono.Unix.Native { 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 @@ namespace Mono.Unix.Native { } } + [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 @@ namespace Mono.Unix.Native { } } + [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 @@ namespace Mono.Unix.Native { #region <socket.h> Declarations // - // <socket.h> + // <socket.h> -- COMPLETE // // socket(2) @@ -5642,6 +5715,155 @@ namespace Mono.Unix.Native { 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 dd5ed9bf40b..c16884dca0c 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 @@ namespace MonoTests.Mono.Unix.Native 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<int> (); + 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 (); + } + }); + }); + } } } |