Skip to content

Commit

Permalink
Merge pull request #2097 from steffen-kiess/posix-sockets-3
Browse files Browse the repository at this point in the history
 [Mono.Posix] Add support for sending and receiving socket control messages
  • Loading branch information
jonpryor committed Jan 25, 2016
2 parents 7fae254 + 2225b6a commit 44e2c53
Show file tree
Hide file tree
Showing 11 changed files with 858 additions and 14 deletions.
2 changes: 2 additions & 0 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -2090,6 +2090,8 @@ if test x$host_win32 = xno; then
#include <unistd.h>])
AC_CHECK_TYPES([suseconds_t], [AC_DEFINE(HAVE_SUSECONDS_T)], ,
[#include <sys/time.h>])
AC_CHECK_TYPES([struct cmsghdr], [AC_DEFINE(HAVE_STRUCT_CMSGHDR)], ,
[#include <sys/socket.h>])
AC_CHECK_TYPES([struct flock], [AC_DEFINE(HAVE_STRUCT_FLOCK)], ,
[#include <unistd.h>
#include <fcntl.h>])
Expand Down
48 changes: 48 additions & 0 deletions mcs/class/Mono.Posix/Mono.Unix.Native/NativeConvert.generated.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,22 @@ public static AtFlags ToAtFlags (Int32 value)
return rval;
}

[DllImport (LIB, EntryPoint="Mono_Posix_FromCmsghdr")]
private static extern int FromCmsghdr (ref Cmsghdr source, IntPtr destination);

public static bool TryCopy (ref Cmsghdr source, IntPtr destination)
{
return FromCmsghdr (ref source, destination) == 0;
}

[DllImport (LIB, EntryPoint="Mono_Posix_ToCmsghdr")]
private static extern int ToCmsghdr (IntPtr source, out Cmsghdr destination);

public static bool TryCopy (IntPtr source, out Cmsghdr destination)
{
return ToCmsghdr (source, out destination) == 0;
}

[DllImport (LIB, EntryPoint="Mono_Posix_FromConfstrName")]
private static extern int FromConfstrName (ConfstrName value, out Int32 rval);

Expand Down Expand Up @@ -1190,6 +1206,38 @@ public static UnixAddressFamily ToUnixAddressFamily (Int32 value)
return rval;
}

[DllImport (LIB, EntryPoint="Mono_Posix_FromUnixSocketControlMessage")]
private static extern int FromUnixSocketControlMessage (UnixSocketControlMessage value, out Int32 rval);

public static bool TryFromUnixSocketControlMessage (UnixSocketControlMessage value, out Int32 rval)
{
return FromUnixSocketControlMessage (value, out rval) == 0;
}

public static Int32 FromUnixSocketControlMessage (UnixSocketControlMessage value)
{
Int32 rval;
if (FromUnixSocketControlMessage (value, out rval) == -1)
ThrowArgumentException (value);
return rval;
}

[DllImport (LIB, EntryPoint="Mono_Posix_ToUnixSocketControlMessage")]
private static extern int ToUnixSocketControlMessage (Int32 value, out UnixSocketControlMessage rval);

public static bool TryToUnixSocketControlMessage (Int32 value, out UnixSocketControlMessage rval)
{
return ToUnixSocketControlMessage (value, out rval) == 0;
}

public static UnixSocketControlMessage ToUnixSocketControlMessage (Int32 value)
{
UnixSocketControlMessage rval;
if (ToUnixSocketControlMessage (value, out rval) == -1)
ThrowArgumentException (value);
return rval;
}

[DllImport (LIB, EntryPoint="Mono_Posix_FromUnixSocketFlags")]
private static extern int FromUnixSocketFlags (UnixSocketFlags value, out Int32 rval);

Expand Down
224 changes: 223 additions & 1 deletion mcs/class/Mono.Posix/Mono.Unix.Native/Syscall.cs
Original file line number Diff line number Diff line change
Expand Up @@ -924,6 +924,13 @@ enum SockaddrType : int {
MustBeWrapped = 0x8000,
}

[Map]
[CLSCompliant (false)]
public enum UnixSocketControlMessage : int {
SCM_RIGHTS = 0x01, /* Transfer file descriptors. */
SCM_CREDENTIALS = 0x02, /* Credentials passing. */
}

#endregion

#region Structures
Expand Down Expand Up @@ -1576,6 +1583,60 @@ public bool Equals (In6Addr value)
}
}

[Map ("struct cmsghdr")]
[CLSCompliant (false)]
public struct Cmsghdr {
public long cmsg_len;
public UnixSocketProtocol cmsg_level;
public UnixSocketControlMessage cmsg_type;

[DllImport (Syscall.MPH, SetLastError=true,
EntryPoint="Mono_Posix_Cmsghdr_getsize")]
static extern int getsize ();
static readonly int size = getsize ();
public static int Size {
get {
return size;
}
}

// Read a struct cmsghdr from msgh.msg_control at offset cmsg and convert it to managed Cmsghdr structure
public static unsafe Cmsghdr ReadFromBuffer (Msghdr msgh, long cmsg)
{
if (msgh == null)
throw new ArgumentNullException ("msgh");
if (msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length)
throw new ArgumentException ("msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length", "msgh");
if (cmsg < 0 || cmsg + Cmsghdr.Size > msgh.msg_controllen)
throw new ArgumentException ("cmsg offset pointing out of buffer", "cmsg");

Cmsghdr hdr;
fixed (byte* ptr = msgh.msg_control)
if (!NativeConvert.TryCopy ((IntPtr) (ptr + cmsg), out hdr))
throw new ArgumentException ("Failed to convert from native struct", "buffer");
// SOL_SOCKET has the same value as IPPROTO_ICMP on linux.
// Make sure that cmsg_level is set to SOL_SOCKET in this case.
if (NativeConvert.FromUnixSocketProtocol (hdr.cmsg_level) == NativeConvert.FromUnixSocketProtocol (UnixSocketProtocol.SOL_SOCKET))
hdr.cmsg_level = UnixSocketProtocol.SOL_SOCKET;
return hdr;
}

// Convert the Cmsghdr to a native struct cmsghdr and write it to msgh.msg_control at offset cmsg
public unsafe void WriteToBuffer (Msghdr msgh, long cmsg)
{
if (msgh == null)
throw new ArgumentNullException ("msgh");
if (msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length)
throw new ArgumentException ("msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length", "msgh");
if (cmsg < 0 || cmsg + Cmsghdr.Size > msgh.msg_controllen)
throw new ArgumentException ("cmsg offset pointing out of buffer", "cmsg");

fixed (byte* ptr = msgh.msg_control)
if (!NativeConvert.TryCopy (ref this, (IntPtr) (ptr + cmsg)))
throw new ArgumentException ("Failed to convert to native struct", "buffer");
}
}

#endregion

#region Classes
Expand Down Expand Up @@ -2386,6 +2447,18 @@ public bool Equals (SockaddrIn6 value)
}
}

[CLSCompliant (false)]
public sealed class Msghdr
{
public Sockaddr msg_name;
// msg_name_len is part of the Sockaddr structure
public Iovec[] msg_iov;
public int msg_iovlen;
public byte[] msg_control;
public long msg_controllen;
public MessageFlags msg_flags;
}

//
// Convention: Functions *not* part of the standard C library AND part of
// a POSIX and/or Unix standard (X/Open, SUS, XPG, etc.) go here.
Expand Down Expand Up @@ -5250,7 +5323,7 @@ public static long pwritev (int fd, Iovec[] iov, long offset)

#region <socket.h> Declarations
//
// <socket.h>
// <socket.h> -- COMPLETE
//

// socket(2)
Expand Down Expand Up @@ -5642,6 +5715,155 @@ public static unsafe long sendto (int socket, byte[] message, ulong length, Mess
return sendto (socket, ptr, length, flags, address);
}

// structure for recvmsg() and sendmsg()
unsafe struct _Msghdr
{
public Iovec* msg_iov;
public int msg_iovlen;
public byte* msg_control;
public long msg_controllen;
public int msg_flags;

public _Msghdr (Msghdr message, Iovec* ptr_msg_iov, byte* ptr_msg_control)
{
if (message.msg_iovlen > message.msg_iov.Length || message.msg_iovlen < 0)
throw new ArgumentException ("message.msg_iovlen > message.msg_iov.Length || message.msg_iovlen < 0", "message");
msg_iov = ptr_msg_iov;
msg_iovlen = message.msg_iovlen;

if (message.msg_control == null && message.msg_controllen != 0)
throw new ArgumentException ("message.msg_control == null && message.msg_controllen != 0", "message");
if (message.msg_control != null && message.msg_controllen > message.msg_control.Length)
throw new ArgumentException ("message.msg_controllen > message.msg_control.Length", "message");
msg_control = ptr_msg_control;
msg_controllen = message.msg_controllen;

msg_flags = 0; // msg_flags is only passed out of the kernel
}

public void Update (Msghdr message)
{
message.msg_controllen = msg_controllen;
message.msg_flags = NativeConvert.ToMessageFlags (msg_flags);
}
}

// recvmsg(2)
// ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags);
[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_recvmsg")]
static extern unsafe long sys_recvmsg (int socket, ref _Msghdr message, _SockaddrHeader* msg_name, int flags);

public static unsafe long recvmsg (int socket, Msghdr message, MessageFlags flags)
{
var _flags = NativeConvert.FromMessageFlags (flags);
var address = message.msg_name;
fixed (byte* ptr_msg_control = message.msg_control)
fixed (Iovec* ptr_msg_iov = message.msg_iov) {
var _message = new _Msghdr (message, ptr_msg_iov, ptr_msg_control);
long r;
fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type)
fixed (byte* data = Sockaddr.GetDynamicData (address)) {
var dyn = new _SockaddrDynamic (address, data, useMaxLength: true);
r = sys_recvmsg (socket, ref _message, Sockaddr.GetNative (&dyn, addr), _flags);
dyn.Update (address);
}
_message.Update (message);
return r;
}
}

// sendmsg(2)
// ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags);
[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_sendmsg")]
static extern unsafe long sys_sendmsg (int socket, ref _Msghdr message, _SockaddrHeader* msg_name, int flags);

public static unsafe long sendmsg (int socket, Msghdr message, MessageFlags flags)
{
var _flags = NativeConvert.FromMessageFlags (flags);
var address = message.msg_name;
fixed (byte* ptr_msg_control = message.msg_control)
fixed (Iovec* ptr_msg_iov = message.msg_iov) {
var _message = new _Msghdr (message, ptr_msg_iov, ptr_msg_control);
fixed (SockaddrType* addr = &Sockaddr.GetAddress (address).type)
fixed (byte* data = Sockaddr.GetDynamicData (address)) {
var dyn = new _SockaddrDynamic (address, data, useMaxLength: false);
return sys_sendmsg (socket, ref _message, Sockaddr.GetNative (&dyn, addr), _flags);
}
}
}

// cmsg(3)
// struct cmsghdr *CMSG_FIRSTHDR(struct msghdr *msgh);
// struct cmsghdr *CMSG_NXTHDR(struct msghdr *msgh, struct cmsghdr *cmsg);
// size_t CMSG_ALIGN(size_t length);
// size_t CMSG_SPACE(size_t length);
// size_t CMSG_LEN(size_t length);
// unsigned char *CMSG_DATA(struct cmsghdr *cmsg);

// Wrapper methods use long offsets into msg_control instead of a
// struct cmsghdr *cmsg pointer because pointers into a byte[] aren't
// stable when the array is not pinned.
// NULL is mapped to -1.

[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_CMSG_FIRSTHDR")]
static extern unsafe long CMSG_FIRSTHDR (byte* msg_control, long msg_controllen);

public static unsafe long CMSG_FIRSTHDR (Msghdr msgh)
{
if (msgh.msg_control == null && msgh.msg_controllen != 0)
throw new ArgumentException ("msgh.msg_control == null && msgh.msg_controllen != 0", "msgh");
if (msgh.msg_control != null && msgh.msg_controllen > msgh.msg_control.Length)
throw new ArgumentException ("msgh.msg_controllen > msgh.msg_control.Length", "msgh");

fixed (byte* ptr = msgh.msg_control)
return CMSG_FIRSTHDR (ptr, msgh.msg_controllen);
}

[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_CMSG_NXTHDR")]
static extern unsafe long CMSG_NXTHDR (byte* msg_control, long msg_controllen, long cmsg);

public static unsafe long CMSG_NXTHDR (Msghdr msgh, long cmsg)
{
if (msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length)
throw new ArgumentException ("msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length", "msgh");
if (cmsg < 0 || cmsg + Cmsghdr.Size > msgh.msg_controllen)
throw new ArgumentException ("cmsg offset pointing out of buffer", "cmsg");

fixed (byte* ptr = msgh.msg_control)
return CMSG_NXTHDR (ptr, msgh.msg_controllen, cmsg);
}

[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_CMSG_DATA")]
static extern unsafe long CMSG_DATA (byte* msg_control, long msg_controllen, long cmsg);

public static unsafe long CMSG_DATA (Msghdr msgh, long cmsg)
{
if (msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length)
throw new ArgumentException ("msgh.msg_control == null || msgh.msg_controllen > msgh.msg_control.Length", "msgh");
if (cmsg < 0 || cmsg + Cmsghdr.Size > msgh.msg_controllen)
throw new ArgumentException ("cmsg offset pointing out of buffer", "cmsg");

fixed (byte* ptr = msgh.msg_control)
return CMSG_DATA (ptr, msgh.msg_controllen, cmsg);
}

[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_CMSG_ALIGN")]
public static extern ulong CMSG_ALIGN (ulong length);

[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_CMSG_SPACE")]
public static extern ulong CMSG_SPACE (ulong length);

[DllImport (MPH, SetLastError=true,
EntryPoint="Mono_Posix_Syscall_CMSG_LEN")]
public static extern ulong CMSG_LEN (ulong length);

#endregion
}

Expand Down
Loading

0 comments on commit 44e2c53

Please sign in to comment.