1
// Copyright 2008 Alp Toker <alp@atoker.com>
2
// This software is made available under the MIT License
3
// See COPYING for details
7
using System.Runtime.InteropServices;
12
using SizeT = System.UIntPtr;
14
using SSizeT = System.IntPtr;
15
// socklen_t: assumed to be 4 bytes
16
// uid_t: assumed to be 4 bytes
18
sealed class UnixStream : Stream //, IDisposable
20
public readonly UnixSocket usock;
22
public UnixStream (int fd)
24
this.usock = new UnixSocket (fd);
27
public UnixStream (UnixSocket usock)
32
public override bool CanRead
39
public override bool CanSeek
46
public override bool CanWrite
53
public override long Length
56
throw new NotImplementedException ("Seeking is not implemented");
60
public override long Position
63
throw new NotImplementedException ("Seeking is not implemented");
65
throw new NotImplementedException ("Seeking is not implemented");
69
public override long Seek (long offset, SeekOrigin origin)
71
throw new NotImplementedException ("Seeking is not implemented");
74
public override void SetLength (long value)
76
throw new NotImplementedException ("Not implemented");
79
public override void Flush ()
83
public override int Read ([In, Out] byte[] buffer, int offset, int count)
85
return usock.Read (buffer, offset, count);
88
public override void Write (byte[] buffer, int offset, int count)
90
usock.Write (buffer, offset, count);
93
unsafe public override int ReadByte ()
96
usock.Read (&value, 1);
100
unsafe public override void WriteByte (byte value)
102
usock.Write (&value, 1);
108
internal const string LIBC = "libc";
110
[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, SetLastError=false)]
111
static extern uint getuid ();
113
[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, SetLastError=false)]
114
static extern uint geteuid ();
116
public static long GetUID ()
118
long uid = getuid ();
122
public static long GetEUID ()
124
long euid = geteuid ();
129
static class UnixError
131
internal const string LIBC = "libc";
133
[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, SetLastError=false)]
134
static extern IntPtr strerror (int errnum);
136
static string GetErrorString (int errnum)
138
IntPtr strPtr = strerror (errnum);
140
if (strPtr == IntPtr.Zero)
141
return "Unknown Unix error";
143
return Marshal.PtrToStringAnsi (strPtr);
146
// FIXME: Don't hard-code this.
149
public static bool ShouldRetry
152
int errno = System.Runtime.InteropServices.Marshal.GetLastWin32Error ();
153
return errno == EINTR;
157
public static Exception GetLastUnixException ()
159
int errno = System.Runtime.InteropServices.Marshal.GetLastWin32Error ();
160
return new Exception (String.Format ("Error {0}: {1}", errno, GetErrorString (errno)));
164
//[StructLayout(LayoutKind.Sequential, Pack=1)]
165
unsafe struct IOVector
167
public IOVector (IntPtr bbase, int length)
169
this.Base = (void*)bbase;
170
this.length = (SizeT)length;
173
//public IntPtr Base;
182
length = (SizeT)value;
188
unsafe class SockAddr
194
unsafe class UnixSocket
196
internal const string LIBC = "libc";
198
// Solaris provides socket functionality in libsocket rather than libc.
199
// We use a dllmap in the .config to deal with this.
200
internal const string LIBSOCKET = "libsocket";
202
public const short AF_UNIX = 1;
203
// FIXME: SOCK_STREAM is 2 on Solaris
204
public const short SOCK_STREAM = 1;
206
[DllImport (LIBC, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
207
internal static extern IntPtr fork ();
209
[DllImport (LIBC, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
210
internal static extern int dup2 (int fd, int fd2);
212
[DllImport (LIBC, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
213
internal static extern int open ([MarshalAs(UnmanagedType.LPStr)] string path, int oflag);
215
[DllImport (LIBC, CallingConvention = CallingConvention.Cdecl, SetLastError = true)]
216
internal static extern IntPtr setsid ();
219
[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
220
internal static extern int close (int fd);
222
[DllImport (LIBSOCKET, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
223
protected static extern int socket (int domain, int type, int protocol);
225
[DllImport (LIBSOCKET, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
226
protected static extern int connect (int sockfd, byte[] serv_addr, uint addrlen);
228
[DllImport (LIBSOCKET, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
229
protected static extern int bind (int sockfd, byte[] my_addr, uint addrlen);
231
[DllImport (LIBSOCKET, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
232
protected static extern int listen (int sockfd, int backlog);
234
//TODO: this prototype is probably wrong, fix it
235
[DllImport (LIBSOCKET, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
236
protected static extern int accept (int sockfd, void* addr, ref uint addrlen);
238
//TODO: confirm and make use of these functions
239
[DllImport (LIBSOCKET, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
240
protected static extern int getsockopt (int s, int optname, IntPtr optval, ref uint optlen);
242
[DllImport (LIBSOCKET, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
243
protected static extern int setsockopt (int s, int optname, IntPtr optval, uint optlen);
245
[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
246
unsafe static extern SSizeT read (int fd, byte* buf, SizeT count);
248
[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
249
unsafe static extern SSizeT write (int fd, byte* buf, SizeT count);
251
[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
252
unsafe static extern SSizeT readv (int fd, IOVector* iov, int iovcnt);
254
[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
255
unsafe static extern SSizeT writev (int fd, IOVector* iov, int iovcnt);
258
//[DllImport (LIBC, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
259
//static extern int vmsplice (int fd, IOVector* iov, uint nr_segs, uint flags);
261
[DllImport (LIBSOCKET, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
262
public static extern SSizeT recvmsg (int s, void* msg, int flags);
264
[DllImport (LIBSOCKET, CallingConvention=CallingConvention.Cdecl, SetLastError=true)]
265
public static extern SSizeT sendmsg (int s, void* msg, int flags);
268
bool ownsHandle = false;
270
public UnixSocket (int handle) : this (handle, false)
274
public UnixSocket (int handle, bool ownsHandle)
276
this.Handle = handle;
277
this.ownsHandle = ownsHandle;
283
//TODO: don't hard-code PF_UNIX and SOCK_STREAM or SocketType.Stream
284
//AddressFamily family, SocketType type, ProtocolType proto
286
int r = socket (AF_UNIX, SOCK_STREAM, 0);
288
throw UnixError.GetLastUnixException ();
296
if (ownsHandle && Handle > 0)
300
protected bool connected = false;
302
//TODO: consider memory management
309
} while (r < 0 && UnixError.ShouldRetry);
312
throw UnixError.GetLastUnixException ();
318
//TODO: consider memory management
319
public void Connect (byte[] remote_end)
324
r = connect (Handle, remote_end, (uint)remote_end.Length);
325
} while (r < 0 && UnixError.ShouldRetry);
328
throw UnixError.GetLastUnixException ();
333
//assigns a name to the socket
334
public void Bind (byte[] local_end)
336
int r = bind (Handle, local_end, (uint)local_end.Length);
338
throw UnixError.GetLastUnixException ();
341
public void Listen (int backlog)
343
int r = listen (Handle, backlog);
345
throw UnixError.GetLastUnixException ();
348
public UnixSocket Accept ()
350
byte[] addr = new byte[110];
351
uint addrlen = (uint)addr.Length;
353
fixed (byte* addrP = addr) {
357
r = accept (Handle, addrP, ref addrlen);
358
} while (r < 0 && UnixError.ShouldRetry);
361
throw UnixError.GetLastUnixException ();
363
//TODO: use the returned addr
364
//string str = Encoding.Default.GetString (addr, 0, (int)addrlen);
365
return new UnixSocket (r, true);
369
unsafe public int Read (byte[] buf, int offset, int count)
371
fixed (byte* bufP = buf)
372
return Read (bufP + offset, count);
375
public int Write (byte[] buf, int offset, int count)
377
fixed (byte* bufP = buf)
378
return Write (bufP + offset, count);
381
unsafe public int Read (byte* bufP, int count)
386
r = (int)read (Handle, bufP, (SizeT)count);
387
} while (r < 0 && UnixError.ShouldRetry);
390
throw UnixError.GetLastUnixException ();
395
public int Write (byte* bufP, int count)
400
r = (int)write (Handle, bufP, (SizeT)count);
401
} while (r < 0 && UnixError.ShouldRetry);
404
throw UnixError.GetLastUnixException ();
409
public int RecvMsg (void* bufP, int flags)
414
r = (int)recvmsg (Handle, bufP, flags);
415
} while (r < 0 && UnixError.ShouldRetry);
418
throw UnixError.GetLastUnixException ();
423
public int SendMsg (void* bufP, int flags)
428
r = (int)sendmsg (Handle, bufP, flags);
429
} while (r < 0 && UnixError.ShouldRetry);
432
throw UnixError.GetLastUnixException ();
437
public int ReadV (IOVector* iov, int count)
439
//FIXME: Handle EINTR here or elsewhere
440
//FIXME: handle r != count
441
//TODO: check offset correctness
443
int r = (int)readv (Handle, iov, count);
445
throw UnixError.GetLastUnixException ();
450
public int WriteV (IOVector* iov, int count)
452
//FIXME: Handle EINTR here or elsewhere
453
//FIXME: handle r != count
454
//TODO: check offset correctness
456
int r = (int)writev (Handle, iov, count);
458
throw UnixError.GetLastUnixException ();
463
public int Write (IOVector[] iov, int offset, int count)
465
//FIXME: Handle EINTR here or elsewhere
466
//FIXME: handle r != count
467
//TODO: check offset correctness
469
fixed (IOVector* bufP = &iov[offset]) {
470
int r = (int)writev (Handle, bufP + offset, count);
472
throw UnixError.GetLastUnixException ();
478
public int Write (IOVector[] iov)
480
return Write (iov, 0, iov.Length);