Logo Search packages:      
Sourcecode: f-spot version File versions

UnixNativeTransport.cs

// Copyright 2006 Alp Toker <alp@atoker.com>
// This software is made available under the MIT License
// See COPYING for details

//We send BSD-style credentials on all platforms
//Doesn't seem to break Linux (but is redundant there)
//This may turn out to be a bad idea
#define HAVE_CMSGCRED

using System;
using System.IO;
using System.Text;

using System.Runtime.InteropServices;

using Mono.Unix;
using Mono.Unix.Native;

namespace NDesk.DBus.Transports
{
      class UnixSocket
      {
            public const short AF_UNIX = 1;
            //TODO: SOCK_STREAM is 2 on Solaris
            public const short SOCK_STREAM = 1;

            //TODO: some of these are provided by libsocket instead of libc on Solaris

            [DllImport ("libc", SetLastError=true)]
                  protected static extern int socket (int domain, int type, int protocol);

            [DllImport ("libc", SetLastError=true)]
                  protected static extern int connect (int sockfd, byte[] serv_addr, uint addrlen);

            [DllImport ("libc", SetLastError=true)]
                  protected static extern int bind (int sockfd, byte[] my_addr, uint addrlen);

            [DllImport ("libc", SetLastError=true)]
                  protected static extern int listen (int sockfd, int backlog);

            //TODO: this prototype is probably wrong, fix it
            [DllImport ("libc", SetLastError=true)]
                  protected static extern int accept (int sockfd, byte[] addr, ref uint addrlen);

            //TODO: confirm and make use of these functions
            [DllImport ("libc", SetLastError=true)]
                  protected static extern int getsockopt (int s, int optname, IntPtr optval, ref uint optlen);

            [DllImport ("libc", SetLastError=true)]
                  protected static extern int setsockopt (int s, int optname, IntPtr optval, uint optlen);

            [DllImport ("libc", SetLastError=true)]
                  public static extern int recvmsg (int s, IntPtr msg, int flags);

            [DllImport ("libc", SetLastError=true)]
                  public static extern int sendmsg (int s, IntPtr msg, int flags);

            public int Handle;

            public UnixSocket (int handle)
            {
                  this.Handle = handle;
            }

            public UnixSocket ()
            {
                  //TODO: don't hard-code PF_UNIX and SOCK_STREAM or SocketType.Stream
                  //AddressFamily family, SocketType type, ProtocolType proto

                  int r = socket (AF_UNIX, SOCK_STREAM, 0);
                  //we should get the Exception from UnixMarshal and throw it here for a better stack trace, but the relevant API seems to be private
                  UnixMarshal.ThrowExceptionForLastErrorIf (r);
                  Handle = r;
            }

            protected bool connected = false;

            //TODO: consider memory management
            public void Connect (byte[] remote_end)
            {
                  int r = connect (Handle, remote_end, (uint)remote_end.Length);
                  //we should get the Exception from UnixMarshal and throw it here for a better stack trace, but the relevant API seems to be private
                  UnixMarshal.ThrowExceptionForLastErrorIf (r);
                  connected = true;
            }

            //assigns a name to the socket
            public void Bind (byte[] local_end)
            {
                  int r = bind (Handle, local_end, (uint)local_end.Length);
                  UnixMarshal.ThrowExceptionForLastErrorIf (r);
            }

            public void Listen (int backlog)
            {
                  int r = listen (Handle, backlog);
                  UnixMarshal.ThrowExceptionForLastErrorIf (r);
            }

            public UnixSocket Accept ()
            {
                  byte[] addr = new byte[110];
                  uint addrlen = (uint)addr.Length;

                  int r = accept (Handle, addr, ref addrlen);
                  UnixMarshal.ThrowExceptionForLastErrorIf (r);
                  //TODO: use the returned addr
                  //TODO: fix probable memory leak here
                  //string str = Encoding.Default.GetString (addr, 0, (int)addrlen);
                  return new UnixSocket (r);
            }
      }

      struct IOVector
      {
            public IntPtr Base;
            public int Length;
      }

      class UnixNativeTransport : UnixTransport
      {
            protected UnixSocket socket;

            public override void Open (string path, bool @abstract)
            {
                  if (String.IsNullOrEmpty (path))
                        throw new ArgumentException ("path");

                  if (@abstract)
                        socket = OpenAbstractUnix (path);
                  else
                        socket = OpenUnix (path);

                  //socket.Blocking = true;
                  SocketHandle = (long)socket.Handle;
                  Stream = new UnixStream ((int)socket.Handle);
            }

            //send peer credentials null byte
            //different platforms do this in different ways
#if HAVE_CMSGCRED
            unsafe void WriteBsdCred ()
            {
                  //null credentials byte
                  byte buf = 0;

                  IOVector iov = new IOVector ();
                  iov.Base = (IntPtr)(&buf);
                  iov.Length = 1;

                  msghdr msg = new msghdr ();
                  msg.msg_iov = &iov;
                  msg.msg_iovlen = 1;

                  cmsg cm = new cmsg ();
                  msg.msg_control = (IntPtr)(&cm);
                  msg.msg_controllen = (uint)sizeof (cmsg);
                  cm.hdr.cmsg_len = (uint)sizeof (cmsg);
                  cm.hdr.cmsg_level = 0xffff; //SOL_SOCKET
                  cm.hdr.cmsg_type = 0x03; //SCM_CREDS

                  int written = UnixSocket.sendmsg (socket.Handle, (IntPtr)(&msg), 0);
                  UnixMarshal.ThrowExceptionForLastErrorIf (written);
                  if (written != 1)
                        throw new Exception ("Failed to write credentials");
            }
#endif

            public override void WriteCred ()
            {
#if HAVE_CMSGCRED
                  try {
                        WriteBsdCred ();
                  } catch {
                        if (Protocol.Verbose)
                              Console.Error.WriteLine ("Warning: WriteBsdCred() failed; falling back to ordinary WriteCred()");
                        //null credentials byte
                        byte buf = 0;
                        Stream.WriteByte (buf);
                  }
#else
                  //null credentials byte
                  byte buf = 0;
                  Stream.WriteByte (buf);
#endif
            }

            protected UnixSocket OpenAbstractUnix (string path)
            {
                  byte[] p = Encoding.Default.GetBytes (path);

                  byte[] sa = new byte[2 + 1 + p.Length];

                  //we use BitConverter to stay endian-safe
                  byte[] afData = BitConverter.GetBytes (UnixSocket.AF_UNIX);
                  sa[0] = afData[0];
                  sa[1] = afData[1];

                  sa[2] = 0; //null prefix for abstract domain socket addresses, see unix(7)
                  for (int i = 0 ; i != p.Length ; i++)
                        sa[3 + i] = p[i];

                  UnixSocket client = new UnixSocket ();
                  client.Connect (sa);

                  return client;
            }

            public UnixSocket OpenUnix (string path)
            {
                  byte[] p = Encoding.Default.GetBytes (path);

                  byte[] sa = new byte[2 + p.Length + 1];

                  //we use BitConverter to stay endian-safe
                  byte[] afData = BitConverter.GetBytes (UnixSocket.AF_UNIX);
                  sa[0] = afData[0];
                  sa[1] = afData[1];

                  for (int i = 0 ; i != p.Length ; i++)
                        sa[2 + i] = p[i];
                  sa[2 + p.Length] = 0; //null suffix for domain socket addresses, see unix(7)

                  UnixSocket client = new UnixSocket ();
                  client.Connect (sa);

                  return client;
            }
      }

#if HAVE_CMSGCRED
      /*
      public struct msg
      {
            public IntPtr msg_next;
            public long msg_type;
            public ushort msg_ts;
            short msg_spot;
            IntPtr label;
      }
      */

      unsafe struct msghdr
      {
            public IntPtr msg_name; //optional address
            public uint msg_namelen; //size of address
            public IOVector *msg_iov; //scatter/gather array
            public int msg_iovlen; //# elements in msg_iov
            public IntPtr msg_control; //ancillary data, see below
            public uint msg_controllen; //ancillary data buffer len
            public int msg_flags; //flags on received message
      }

      struct cmsghdr
      {
            public uint cmsg_len; //data byte count, including header
            public int cmsg_level; //originating protocol
            public int cmsg_type; //protocol-specific type
      }

      unsafe struct cmsgcred
      {
            public int cmcred_pid; //PID of sending process
            public uint cmcred_uid; //real UID of sending process
            public uint cmcred_euid; //effective UID of sending process
            public uint cmcred_gid; //real GID of sending process
            public short cmcred_ngroups; //number or groups
            public fixed uint cmcred_groups[16]; //groups, CMGROUP_MAX
      }

      struct cmsg
      {
            public cmsghdr hdr;
            public cmsgcred cred;
      }
#endif
}

Generated by  Doxygen 1.6.0   Back to index