Exemple #1
0
        /// <summary>
        /// Helper method to set up the socket when calling bind
        /// </summary>
        /// <param name="family">The address family to use</param>
        private void SetupSocket(UnixAddressFamily family)
        {
            if (m_socket != -1)
            {
                throw new InvalidOperationException("The socket is already initialized");
            }

            // Create new socket
            m_socket = Syscall.socket(family, UnixSocketType.SOCK_STREAM, 0);

            // Allow address reuse
            Syscall.setsockopt(m_socket, UnixSocketProtocol.SOL_SOCKET, UnixSocketOptionName.SO_REUSEADDR, 1);

            var opts = Syscall.fcntl(m_socket, FcntlCommand.F_GETFL);

            if (opts < 0)
            {
                throw new IOException($"Failed to get openflags from handle: {Stdlib.GetLastError()}");
            }

            opts |= (int)OpenFlags.O_NONBLOCK;

            if (Syscall.fcntl(m_socket, FcntlCommand.F_SETFL, opts) < 0)
            {
                throw new IOException($"Failed to set socket O_NOBLOCK: {Stdlib.GetLastError()}");
            }

            m_handle = m_handler.MonitoredHandle(m_socket);
        }
        /// <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);
        }