/// <summary>
        /// Sends one or more file descriptors over the given socket with SCM_RIGHTS.
        /// </summary>
        /// <param name="sock">The socket handle to use.</param>
        /// <param name="fds">The file descriptors to send. Must contain at least one.</param>
        /// <param name="buffer">The data to send with the message, cannot be null or empty.</param>
        public static void send_fds(int sock, int[] fds, byte[] buffer)
        {
            if (fds == null || fds.Length == 0)
            {
                throw new ArgumentException("At least one file descriptor must be sent");
            }

            if (buffer == null || buffer.Length == 0)
            {
                throw new ArgumentException("The buffer cannot be empty");
            }

            // Create the SCM_RIGHTS control message
            var cmsgbuffer = new byte[Mono.Unix.Native.Syscall.CMSG_SPACE((ulong)fds.Length * sizeof(int))];
            var cmsghdr    = new Mono.Unix.Native.Cmsghdr
            {
                cmsg_len   = (long)Mono.Unix.Native.Syscall.CMSG_LEN((ulong)fds.Length * sizeof(int)),
                cmsg_level = Mono.Unix.Native.UnixSocketProtocol.SOL_SOCKET,
                cmsg_type  = Mono.Unix.Native.UnixSocketControlMessage.SCM_RIGHTS,
            };

            // Create the message header
            var msghdr = new Mono.Unix.Native.Msghdr
            {
                msg_control    = cmsgbuffer,
                msg_controllen = cmsgbuffer.Length,
            };

            cmsghdr.WriteToBuffer(msghdr, 0);

            // Copy in the file handles
            var dataOffset = Mono.Unix.Native.Syscall.CMSG_DATA(msghdr, 0);

            for (var i = 0; i < fds.Length; i++)
            {
                Array.Copy(BitConverter.GetBytes(fds[i]), 0, msghdr.msg_control, dataOffset, sizeof(int));
                dataOffset += sizeof(int);
            }

            // Pin the buffer we are sending
            using (var ptr_buffer1 = new GuardedHandle(buffer))
            {
                // Put the buffer into the IO vector attached to the message
                var iovecs = new[] {
                    new Mono.Unix.Native.Iovec {
                        iov_base = ptr_buffer1.Address,
                        iov_len  = (ulong)buffer.Length,
                    },
                };
                msghdr.msg_iov    = iovecs;
                msghdr.msg_iovlen = 1;

                // Send it
                var ret = Mono.Unix.Native.Syscall.sendmsg(sock, msghdr, 0);
                if (ret < 0)
                {
                    Mono.Unix.UnixMarshal.ThrowExceptionForLastError();
                }
            }
        }
示例#2
0
 /// <summary>
 /// Creates a new EventFile
 /// </summary>
 public EventFile()
 {
     m_bufferHandle = new GuardedHandle(m_buffer);
     Handle         = PInvoke.eventfd(0, 0);
 }
        /// <summary>
        /// Reads file descriptors and the message from a SCM_RIGHTS message
        /// </summary>
        /// <returns>The file descriptors and the bytes.</returns>
        /// <param name="handle">The socket to read from.</param>
        public static async Task <List <Tuple <int[], byte[]> > > recv_fds_async(MonitoredHandle handle)
        {
            var buffer = new byte[1024];
            var cmsg   = new byte[1024];
            var msghdr = new Mono.Unix.Native.Msghdr
            {
                msg_control    = cmsg,
                msg_controllen = cmsg.Length,
            };

            long datalen;

            await handle.WaitForReadAsync;

            var res = new List <Tuple <int[], byte[]> >();

            using (var ptr_buffer = new GuardedHandle(buffer))
            {
                var iovec = new [] {
                    new Mono.Unix.Native.Iovec {
                        iov_base = ptr_buffer.Address,
                        iov_len  = (ulong)buffer.Length,
                    },
                };
                msghdr.msg_iov    = iovec;
                msghdr.msg_iovlen = 1;

                DebugHelper.WriteLine("Calling recvmsg");
                while ((datalen = Mono.Unix.Native.Syscall.recvmsg(handle.Handle, msghdr, 0)) != -1)
                {
                    DebugHelper.WriteLine("Called recvmsg: {0}", datalen);
                    if (datalen == 0)
                    {
                        DebugHelper.WriteLine("No more data, stopping recv_fds");
                        handle.Dispose();
                        break;
                    }

                    if (datalen < 0)
                    {
                        Mono.Unix.UnixMarshal.ThrowExceptionForLastError();
                    }

                    // Get the offset of the first message
                    var offset = Mono.Unix.Native.Syscall.CMSG_FIRSTHDR(msghdr);
                    DebugHelper.WriteLine("Called recvmsg, offset: {0}", offset);

                    // Extract the bytes
                    var recvHdr = Mono.Unix.Native.Cmsghdr.ReadFromBuffer(msghdr, offset);
                    //DebugHelper.WriteLine("Buffer has, recvHdr.cmsg_len: {0}", recvHdr.cmsg_len);

                    var data = new byte[datalen];
                    Array.Copy(buffer, data, data.Length);
                    //DebugHelper.WriteLine("Got {0} bytes of data", data.Length);
                    //foreach (var n in data)
                    //    Console.Write("{0}, ", n);
                    //DebugHelper.WriteLine();

                    // See how many bytes are of file descriptors we have
                    var recvDataOffset = Mono.Unix.Native.Syscall.CMSG_DATA(msghdr, offset);
                    //var userData = recvHdr.cmsg_len - (int)Mono.Unix.Native.Syscall.CMSG_LEN(0);
                    var bytes   = recvHdr.cmsg_len - (recvDataOffset - offset);
                    var fdCount = bytes / sizeof(int);
                    var fds     = new int[fdCount];
                    //DebugHelper.WriteLine("Got {0} fds ({1} bytes)", fdCount, bytes);

                    // Extract the file descriptors
                    for (int i = 0; i < fdCount; i++)
                    {
                        fds[i] = BitConverter.ToInt32(msghdr.msg_control, (int)(recvDataOffset + (sizeof(int) * i)));
                        //DebugHelper.WriteLine($"fd[{i}] = {fds[i]}");
                    }

                    //DebugHelper.WriteLine("Read {0} fds and {1} bytes", fds.Length, data.Length);

                    // Check that we only have a single message
                    offset = Mono.Unix.Native.Syscall.CMSG_NXTHDR(msghdr, offset);
                    if (offset != -1)
                    {
                        System.Diagnostics.Trace.WriteLine("WARNING: more than one message detected when reading SCM_RIGHTS, only processing the first one");
                    }


                    res.Add(new Tuple <int[], byte[]>(fds, data));
                }
            }
            var lasterr = Mono.Unix.Native.Stdlib.GetLastError();

            if (res.Count == 0 && datalen < 0 && lasterr != Mono.Unix.Native.Errno.EAGAIN && lasterr != Mono.Unix.Native.Errno.EWOULDBLOCK)
            {
                throw new System.IO.IOException($"Unexpected error code while reading socket: {lasterr}");
            }

            return(res);
        }
        /// <summary>
        /// Reads file descriptors and the message from a SCM_RIGHTS message
        /// </summary>
        /// <returns>The file descriptors and the bytes.</returns>
        /// <param name="sock">The socket to read from.</param>
        public static Tuple <int[], byte[]> recv_fds(int sock)
        {
            var buffer = new byte[1024];
            var cmsg   = new byte[1024];
            var msghdr = new Mono.Unix.Native.Msghdr
            {
                msg_control    = cmsg,
                msg_controllen = cmsg.Length,
            };

            long datalen;

            using (var ptr_buffer = new GuardedHandle(buffer))
            {
                var iovec = new[] {
                    new Mono.Unix.Native.Iovec {
                        iov_base = ptr_buffer.Address,
                        iov_len  = (ulong)buffer.Length,
                    },
                };
                msghdr.msg_iov    = iovec;
                msghdr.msg_iovlen = 1;
                DebugHelper.WriteLine("{0}: Calling recvmsg", System.Diagnostics.Process.GetCurrentProcess().Id);
                datalen = Mono.Unix.Native.Syscall.recvmsg(sock, msghdr, 0);
                DebugHelper.WriteLine("{0}: Called recvmsg: {1}", System.Diagnostics.Process.GetCurrentProcess().Id, datalen);
                if (datalen == 0)
                {
                    return(null);
                }
                if (datalen < 0)
                {
                    Mono.Unix.UnixMarshal.ThrowExceptionForLastError();
                }
            }

            // Get the offset of the first message
            var offset = Mono.Unix.Native.Syscall.CMSG_FIRSTHDR(msghdr);

            DebugHelper.WriteLine("{0}: Called recvmsg, offset: {1}", System.Diagnostics.Process.GetCurrentProcess().Id, offset);

            // Extract the bytes
            var recvHdr = Mono.Unix.Native.Cmsghdr.ReadFromBuffer(msghdr, offset);
            //DebugHelper.WriteLine("Buffer has, recvHdr.cmsg_len: {0}", recvHdr.cmsg_len);

            var data = new byte[datalen];

            Array.Copy(buffer, data, data.Length);
            //DebugHelper.WriteLine("Got {0} bytes of data", data.Length);
            //foreach (var n in data)
            //    Console.Write("{0}, ", n);
            //DebugHelper.WriteLine();

            // See how many bytes are of file descriptors we have
            var recvDataOffset = Mono.Unix.Native.Syscall.CMSG_DATA(msghdr, offset);
            //var userData = recvHdr.cmsg_len - (int)Mono.Unix.Native.Syscall.CMSG_LEN(0);
            var bytes   = recvHdr.cmsg_len - (recvDataOffset - offset);
            var fdCount = bytes / sizeof(int);
            var fds     = new int[fdCount];

            //DebugHelper.WriteLine("Got {0} fds ({1} bytes)", fdCount, bytes);

            // Extract the file descriptors
            for (int i = 0; i < fdCount; i++)
            {
                fds[i] = BitConverter.ToInt32(msghdr.msg_control, (int)(recvDataOffset + (sizeof(int) * i)));
                //DebugHelper.WriteLine($"fd[{i}] = {fds[i]}");
            }

            //DebugHelper.WriteLine("Read {0} fds and {1} bytes", fds.Length, data.Length);

            // Check that we only have a single message
            offset = Mono.Unix.Native.Syscall.CMSG_NXTHDR(msghdr, offset);
            if (offset != -1)
            {
                System.Diagnostics.Trace.WriteLine("WARNING: more than one message detected when reading SCM_RIGHTS, only processing the first one");
            }

            return(new Tuple <int[], byte[]>(fds, data));
        }