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/Test | |
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/Test')
-rw-r--r-- | mcs/class/Mono.Posix/Test/Mono.Unix.Native/SocketTest.cs | 286 |
1 files changed, 286 insertions, 0 deletions
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 (); + } + }); + }); + } } } |