2
// MonoMac.CoreFoundation.CFSocket
5
// Martin Baulig (martin.baulig@xamarin.com)
7
// Copyright 2012 Xamarin Inc. (http://www.xamarin.com)
10
// Permission is hereby granted, free of charge, to any person obtaining
11
// a copy of this software and associated documentation files (the
12
// "Software"), to deal in the Software without restriction, including
13
// without limitation the rights to use, copy, modify, merge, publish,
14
// distribute, sublicense, and/or sell copies of the Software, and to
15
// permit persons to whom the Software is furnished to do so, subject to
16
// the following conditions:
18
// The above copyright notice and this permission notice shall be
19
// included in all copies or substantial portions of the Software.
21
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
22
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
23
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
24
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
25
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
26
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
27
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32
using System.Net.Sockets;
33
using System.Runtime.InteropServices;
34
using MonoMac.CoreFoundation;
35
using MonoMac.ObjCRuntime;
37
namespace MonoMac.CoreFoundation {
40
public enum CFSocketCallBackType {
49
public enum CFSocketError {
56
public enum CFSocketFlags {
57
AutomaticallyReenableReadCallBack = 1,
58
AutomaticallyReenableAcceptCallBack = 2,
59
AutomaticallyReenableDataCallBack = 3,
60
AutomaticallyReenableWriteCallBack = 8,
62
CloseOnInvalidate = 128
65
public struct CFSocketNativeHandle {
66
internal readonly int handle;
68
internal CFSocketNativeHandle (int handle)
73
public override string ToString ()
75
return string.Format ("[CFSocketNativeHandle {0}]", handle);
79
public class CFSocketException : Exception {
80
public CFSocketError Error {
85
public CFSocketException (CFSocketError error)
91
struct CFSocketSignature {
97
public CFSocketSignature (AddressFamily family, SocketType type,
98
ProtocolType proto, CFSocketAddress address)
100
this.protocolFamily = AddressFamilyToInt (family);
101
this.socketType = SocketTypeToInt (type);
102
this.protocol = ProtocolToInt (proto);
103
this.address = address.Handle;
106
internal static int AddressFamilyToInt (AddressFamily family)
109
case AddressFamily.Unspecified:
111
case AddressFamily.Unix:
113
case AddressFamily.InterNetwork:
115
case AddressFamily.AppleTalk:
117
case AddressFamily.InterNetworkV6:
120
throw new ArgumentException ();
124
internal static int SocketTypeToInt (SocketType type)
130
case SocketType.Unknown:
132
case SocketType.Stream:
134
case SocketType.Dgram:
140
case SocketType.Seqpacket:
143
throw new ArgumentException ();
147
internal static int ProtocolToInt (ProtocolType type)
154
class CFSocketAddress : CFDataBuffer {
155
public CFSocketAddress (IPEndPoint endpoint)
156
: base (CreateData (endpoint))
160
internal static IPEndPoint EndPointFromAddressPtr (IntPtr address)
162
using (var buffer = new CFDataBuffer (address)) {
163
if (buffer [1] == 30) { // AF_INET6
164
int port = (buffer [2] << 8) + buffer [3];
165
var bytes = new byte [16];
166
Buffer.BlockCopy (buffer.Data, 8, bytes, 0, 16);
167
return new IPEndPoint (new IPAddress (bytes), port);
168
} else if (buffer [1] == 2) { // AF_INET
169
int port = (buffer [2] << 8) + buffer [3];
170
var bytes = new byte [4];
171
Buffer.BlockCopy (buffer.Data, 4, bytes, 0, 4);
172
return new IPEndPoint (new IPAddress (bytes), port);
174
throw new ArgumentException ();
179
static byte[] CreateData (IPEndPoint endpoint)
181
if (endpoint.AddressFamily == AddressFamily.InterNetwork) {
182
var buffer = new byte [16];
184
buffer [1] = 2; // AF_INET
185
buffer [2] = (byte)(endpoint.Port >> 8);
186
buffer [3] = (byte)(endpoint.Port & 0xff);
187
Buffer.BlockCopy (endpoint.Address.GetAddressBytes (), 0, buffer, 4, 4);
189
} else if (endpoint.AddressFamily == AddressFamily.InterNetworkV6) {
190
var buffer = new byte [28];
192
buffer [1] = 30; // AF_INET6
193
buffer [2] = (byte)(endpoint.Port >> 8);
194
buffer [3] = (byte)(endpoint.Port & 0xff);
195
Buffer.BlockCopy (endpoint.Address.GetAddressBytes (), 0, buffer, 8, 16);
198
throw new ArgumentException ();
203
public class CFSocket : CFType, INativeObject, IDisposable {
212
public void Dispose ()
215
GC.SuppressFinalize (this);
218
public IntPtr Handle {
219
get { return handle; }
222
protected virtual void Dispose (bool disposing)
228
if (handle != IntPtr.Zero) {
229
CFObject.CFRelease (handle);
230
handle = IntPtr.Zero;
234
delegate void CFSocketCallBack (IntPtr s, int type, IntPtr address, IntPtr data, IntPtr info);
236
[MonoPInvokeCallback (typeof(CFSocketCallBack))]
237
static void OnCallback (IntPtr s, int type, IntPtr address, IntPtr data, IntPtr info)
239
var socket = GCHandle.FromIntPtr (info).Target as CFSocket;
240
CFSocketCallBackType cbType = (CFSocketCallBackType)type;
242
if (cbType == CFSocketCallBackType.AcceptCallBack) {
243
var ep = CFSocketAddress.EndPointFromAddressPtr (address);
244
var handle = new CFSocketNativeHandle (Marshal.ReadInt32 (data));
245
socket.OnAccepted (new CFSocketAcceptEventArgs (handle, ep));
246
} else if (cbType == CFSocketCallBackType.ConnectCallBack) {
247
CFSocketError result;
248
if (data == IntPtr.Zero)
249
result = CFSocketError.Success;
251
result = (CFSocketError)Marshal.ReadInt32 (data);
252
socket.OnConnect (new CFSocketConnectEventArgs (result));
256
[DllImport (Constants.CoreFoundationLibrary)]
257
extern static IntPtr CFSocketCreate (IntPtr allocator, int family, int type, int proto,
258
CFSocketCallBackType callBackTypes,
259
CFSocketCallBack callout, IntPtr ctx);
261
[DllImport (Constants.CoreFoundationLibrary)]
262
extern static IntPtr CFSocketCreateWithNative (IntPtr allocator, CFSocketNativeHandle sock,
263
CFSocketCallBackType callBackTypes,
264
CFSocketCallBack callout, IntPtr ctx);
266
[DllImport (Constants.CoreFoundationLibrary)]
267
extern static IntPtr CFSocketCreateRunLoopSource (IntPtr allocator, IntPtr socket, int order);
274
public CFSocket (AddressFamily family, SocketType type, ProtocolType proto)
275
: this (family, type, proto, CFRunLoop.Current)
279
public CFSocket (AddressFamily family, SocketType type, ProtocolType proto, CFRunLoop loop)
280
: this (CFSocketSignature.AddressFamilyToInt (family),
281
CFSocketSignature.SocketTypeToInt (type),
282
CFSocketSignature.ProtocolToInt (proto), loop)
286
CFSocket (int family, int type, int proto, CFRunLoop loop)
288
var cbTypes = CFSocketCallBackType.DataCallBack | CFSocketCallBackType.ConnectCallBack;
290
gch = GCHandle.Alloc (this);
291
var ctx = new CFStreamClientContext ();
292
ctx.Info = GCHandle.ToIntPtr (gch);
294
var ptr = Marshal.AllocHGlobal (Marshal.SizeOf (typeof(CFStreamClientContext)));
296
Marshal.StructureToPtr (ctx, ptr, false);
297
handle = CFSocketCreate (
298
IntPtr.Zero, family, type, proto, cbTypes, OnCallback, ptr);
300
Marshal.FreeHGlobal (ptr);
303
if (handle == IntPtr.Zero)
304
throw new CFSocketException (CFSocketError.Error);
305
gch = GCHandle.Alloc (this);
307
var source = new CFRunLoopSource (CFSocketCreateRunLoopSource (IntPtr.Zero, handle, 0));
308
loop.AddSource (source, CFRunLoop.CFDefaultRunLoopMode);
311
internal CFSocket (CFSocketNativeHandle sock)
313
var cbTypes = CFSocketCallBackType.DataCallBack | CFSocketCallBackType.WriteCallBack;
315
gch = GCHandle.Alloc (this);
316
var ctx = new CFStreamClientContext ();
317
ctx.Info = GCHandle.ToIntPtr (gch);
319
var ptr = Marshal.AllocHGlobal (Marshal.SizeOf (typeof(CFStreamClientContext)));
321
Marshal.StructureToPtr (ctx, ptr, false);
322
handle = CFSocketCreateWithNative (
323
IntPtr.Zero, sock, cbTypes, OnCallback, ptr);
325
Marshal.FreeHGlobal (ptr);
328
if (handle == IntPtr.Zero)
329
throw new CFSocketException (CFSocketError.Error);
331
var source = new CFRunLoopSource (CFSocketCreateRunLoopSource (IntPtr.Zero, handle, 0));
332
var loop = CFRunLoop.Current;
333
loop.AddSource (source, CFRunLoop.CFDefaultRunLoopMode);
336
CFSocket (IntPtr handle)
338
this.handle = handle;
339
gch = GCHandle.Alloc (this);
341
var source = new CFRunLoopSource (CFSocketCreateRunLoopSource (IntPtr.Zero, handle, 0));
342
var loop = CFRunLoop.Current;
343
loop.AddSource (source, CFRunLoop.CFDefaultRunLoopMode);
346
[DllImport (Constants.CoreFoundationLibrary)]
347
extern static IntPtr CFSocketCreateConnectedToSocketSignature (IntPtr allocator, ref CFSocketSignature signature,
348
CFSocketCallBackType callBackTypes,
349
CFSocketCallBack callout,
350
IntPtr context, double timeout);
352
public static CFSocket CreateConnectedToSocketSignature (AddressFamily family, SocketType type,
353
ProtocolType proto, IPEndPoint endpoint,
356
var cbTypes = CFSocketCallBackType.ConnectCallBack | CFSocketCallBackType.DataCallBack;
357
using (var address = new CFSocketAddress (endpoint)) {
358
var sig = new CFSocketSignature (family, type, proto, address);
359
var handle = CFSocketCreateConnectedToSocketSignature (
360
IntPtr.Zero, ref sig, cbTypes, OnCallback, IntPtr.Zero, timeout);
361
if (handle == IntPtr.Zero)
362
throw new CFSocketException (CFSocketError.Error);
364
return new CFSocket (handle);
368
[DllImport (Constants.CoreFoundationLibrary)]
369
extern static CFSocketNativeHandle CFSocketGetNative (IntPtr handle);
371
internal CFSocketNativeHandle GetNative ()
373
return CFSocketGetNative (handle);
376
[DllImport (Constants.CoreFoundationLibrary)]
377
extern static CFSocketError CFSocketSetAddress (IntPtr handle, IntPtr address);
379
public void SetAddress (IPAddress address, int port)
381
SetAddress (new IPEndPoint (address, port));
384
public void SetAddress (IPEndPoint endpoint)
386
EnableCallBacks (CFSocketCallBackType.AcceptCallBack);
387
var flags = GetSocketFlags ();
388
flags |= CFSocketFlags.AutomaticallyReenableAcceptCallBack;
389
SetSocketFlags (flags);
390
using (var address = new CFSocketAddress (endpoint)) {
391
var error = CFSocketSetAddress (handle, address.Handle);
392
if (error != CFSocketError.Success)
393
throw new CFSocketException (error);
397
[DllImport (Constants.CoreFoundationLibrary)]
398
extern static CFSocketFlags CFSocketGetSocketFlags (IntPtr handle);
400
public CFSocketFlags GetSocketFlags ()
402
return CFSocketGetSocketFlags (handle);
405
[DllImport (Constants.CoreFoundationLibrary)]
406
extern static void CFSocketSetSocketFlags (IntPtr handle, CFSocketFlags flags);
408
public void SetSocketFlags (CFSocketFlags flags)
410
CFSocketSetSocketFlags (handle, flags);
413
[DllImport (Constants.CoreFoundationLibrary)]
414
extern static void CFSocketDisableCallBacks (IntPtr handle, CFSocketCallBackType types);
416
public void DisableCallBacks (CFSocketCallBackType types)
418
CFSocketDisableCallBacks (handle, types);
421
[DllImport (Constants.CoreFoundationLibrary)]
422
extern static void CFSocketEnableCallBacks (IntPtr handle, CFSocketCallBackType types);
424
public void EnableCallBacks (CFSocketCallBackType types)
426
CFSocketEnableCallBacks (handle, types);
429
[DllImport (Constants.CoreFoundationLibrary)]
430
extern static CFSocketError CFSocketSendData (IntPtr handle, IntPtr address, IntPtr data, double timeout);
432
public void SendData (byte[] data, double timeout)
434
using (var buffer = new CFDataBuffer (data)) {
435
var error = CFSocketSendData (handle, IntPtr.Zero, buffer.Handle, timeout);
436
if (error != CFSocketError.Success)
437
throw new CFSocketException (error);
441
public class CFSocketAcceptEventArgs : EventArgs {
442
internal CFSocketNativeHandle SocketHandle {
447
public IPEndPoint RemoteEndPoint {
452
public CFSocketAcceptEventArgs (CFSocketNativeHandle handle, IPEndPoint remote)
454
this.SocketHandle = handle;
455
this.RemoteEndPoint = remote;
458
public CFSocket CreateSocket ()
460
return new CFSocket (SocketHandle);
463
public override string ToString ()
465
return string.Format ("[CFSocketAcceptEventArgs: RemoteEndPoint={0}]", RemoteEndPoint);
469
public class CFSocketConnectEventArgs : EventArgs {
470
public CFSocketError Result {
475
public CFSocketConnectEventArgs (CFSocketError result)
477
this.Result = result;
480
public override string ToString ()
482
return string.Format ("[CFSocketConnectEventArgs: Result={0}]", Result);
486
public event EventHandler<CFSocketAcceptEventArgs> AcceptEvent;
487
public event EventHandler<CFSocketConnectEventArgs> ConnectEvent;
489
void OnAccepted (CFSocketAcceptEventArgs args)
491
if (AcceptEvent != null)
492
AcceptEvent (this, args);
495
void OnConnect (CFSocketConnectEventArgs args)
497
if (ConnectEvent != null)
498
ConnectEvent (this, args);
501
[DllImport (Constants.CoreFoundationLibrary)]
502
extern static CFSocketError CFSocketConnectToAddress (IntPtr handle, IntPtr address, double timeout);
504
public void Connect (IPAddress address, int port, double timeout)
506
Connect (new IPEndPoint (address, port), timeout);
509
public void Connect (IPEndPoint endpoint, double timeout)
511
using (var address = new CFSocketAddress (endpoint)) {
512
var error = CFSocketConnectToAddress (handle, address.Handle, timeout);
513
if (error != CFSocketError.Success)
514
throw new CFSocketException (error);