// Might return short reads
        unsafe int ReadShort(byte[] buffer, int offset, int length, UnixFDArray fdArray)
        {
            if (length < 0 || offset < 0 || length + offset < length || length + offset > buffer.Length)
            {
                throw new ArgumentException();

                fixed(byte *ptr = buffer, cmsgPtr = cmsgBuffer)
                {
                    var iovecs = new Iovec[] {
                        new Iovec {
                            iov_base = (IntPtr)(ptr + offset),
                            iov_len  = (ulong)length,
                        },
                    };

                    var msghdr = new Msghdr {
                        msg_iov        = iovecs,
                        msg_iovlen     = 1,
                        msg_control    = cmsgBuffer,
                        msg_controllen = cmsgBuffer.Length,
                    };

                    long r;

                    do
                    {
                        r = Syscall.recvmsg((int)SocketHandle, msghdr, 0);
                    } while (UnixMarshal.ShouldRetrySyscall((int)r));

                    for (long cOffset = Syscall.CMSG_FIRSTHDR(msghdr); cOffset != -1; cOffset = Syscall.CMSG_NXTHDR(msghdr, cOffset))
                    {
                        var recvHdr = Cmsghdr.ReadFromBuffer(msghdr, cOffset);
                        if (recvHdr.cmsg_level != UnixSocketProtocol.SOL_SOCKET)
                        {
                            continue;
                        }
                        if (recvHdr.cmsg_type != UnixSocketControlMessage.SCM_RIGHTS)
                        {
                            continue;
                        }
                        var recvDataOffset = Syscall.CMSG_DATA(msghdr, cOffset);
                        var bytes          = recvHdr.cmsg_len - (recvDataOffset - cOffset);
                        var fdCount        = bytes / sizeof(int);
                        for (int i = 0; i < fdCount; i++)
                        {
                            fdArray.FDs.Add(new UnixFD(((int *)(cmsgPtr + recvDataOffset))[i]));
                        }
                    }

                    if ((msghdr.msg_flags & MessageFlags.MSG_CTRUNC) != 0)
                    {
                        throw new Exception("Control message truncated (probably file descriptors lost)");
                    }

                    return((int)r);
                }
        }
Esempio n. 2
0
        public unsafe void ControlMsg(bool useMultipleControlMessages)
        {
            // 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) => {
                    byte[] cmsg;
                    Msghdr msghdr1;
                    long offset;
                    if (useMultipleControlMessages)
                    {
                        // Create two SCM_RIGHTS control messages
                        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,
                        };
                        msghdr1 = new Msghdr {
                            msg_control    = cmsg,
                            msg_controllen = cmsg.Length,
                        };
                        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;
                        }
                    }
                    else
                    {
                        // Create one SCM_RIGHTS control message
                        cmsg    = new byte[Syscall.CMSG_SPACE(2 * sizeof(int))];
                        var hdr = new Cmsghdr {
                            cmsg_len   = (long)Syscall.CMSG_LEN(2 * sizeof(int)),
                            cmsg_level = UnixSocketProtocol.SOL_SOCKET,
                            cmsg_type  = UnixSocketControlMessage.SCM_RIGHTS,
                        };
                        msghdr1 = new Msghdr {
                            msg_control    = cmsg,
                            msg_controllen = cmsg.Length,
                        };
                        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;
                            ((int *)(ptr + dataOffset))[1] = 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();
                    }

                    if (useMultipleControlMessages)                                        // This assertion fails on OSX for some reason
                    {
                        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();
                            }
                        }
                    }
                });
            });
        }