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

github.com/mono/mono.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSteffen Kieß <s-kiess@web.de>2016-01-19 22:43:07 +0300
committerSteffen Kieß <s-kiess@web.de>2016-01-19 22:43:07 +0300
commit2225b6a260e1f0d469e37ec0df68a89e832688cd (patch)
treecce4e43c0ac7fbcbfe940222a58faf800a41522b /mcs/class/Mono.Posix
parent5436bc0e5ce16bcb2359c3ef9202e5e46f6ec115 (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')
-rw-r--r--mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs48
-rw-r--r--mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs224
-rw-r--r--mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs286
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 ();
+ }
+ });
+ });
+ }
}
}