/// <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); }