~ubuntu-branches/ubuntu/trusty/monodevelop/trusty-proposed

« back to all changes in this revision

Viewing changes to external/maccore/src/CoreFoundation/CFSocket.cs

  • Committer: Package Import Robot
  • Author(s): Jo Shields
  • Date: 2013-05-12 09:46:03 UTC
  • mto: This revision was merged to the branch mainline in revision 29.
  • Revision ID: package-import@ubuntu.com-20130512094603-mad323bzcxvmcam0
Tags: upstream-4.0.5+dfsg
ImportĀ upstreamĀ versionĀ 4.0.5+dfsg

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
//
 
2
// MonoMac.CoreFoundation.CFSocket
 
3
//
 
4
// Authors:
 
5
//      Martin Baulig (martin.baulig@xamarin.com)
 
6
//
 
7
// Copyright 2012 Xamarin Inc. (http://www.xamarin.com)
 
8
//
 
9
//
 
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:
 
17
// 
 
18
// The above copyright notice and this permission notice shall be
 
19
// included in all copies or substantial portions of the Software.
 
20
// 
 
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.
 
28
//
 
29
 
 
30
using System;
 
31
using System.Net;
 
32
using System.Net.Sockets;
 
33
using System.Runtime.InteropServices;
 
34
using MonoMac.CoreFoundation;
 
35
using MonoMac.ObjCRuntime;
 
36
 
 
37
namespace MonoMac.CoreFoundation {
 
38
 
 
39
        [Flags]
 
40
        public enum CFSocketCallBackType {
 
41
                NoCallBack = 0,
 
42
                ReadCallBack = 1,
 
43
                AcceptCallBack = 2,
 
44
                DataCallBack = 3,
 
45
                ConnectCallBack = 4,
 
46
                WriteCallBack = 8
 
47
        }
 
48
 
 
49
        public enum CFSocketError {
 
50
                Success = 0,
 
51
                Error = -1,
 
52
                Timeout = -2
 
53
        }
 
54
 
 
55
        [Flags]
 
56
        public enum CFSocketFlags {
 
57
                AutomaticallyReenableReadCallBack = 1,
 
58
                AutomaticallyReenableAcceptCallBack = 2,
 
59
                AutomaticallyReenableDataCallBack = 3,
 
60
                AutomaticallyReenableWriteCallBack = 8,
 
61
                LeaveErrors = 64,
 
62
                CloseOnInvalidate = 128
 
63
        }
 
64
 
 
65
        public struct CFSocketNativeHandle {
 
66
                internal readonly int handle;
 
67
 
 
68
                internal CFSocketNativeHandle (int handle)
 
69
                {
 
70
                        this.handle = handle;
 
71
                }
 
72
 
 
73
                public override string ToString ()
 
74
                {
 
75
                        return string.Format ("[CFSocketNativeHandle {0}]", handle);
 
76
                }
 
77
        }
 
78
 
 
79
        public class CFSocketException : Exception {
 
80
                public CFSocketError Error {
 
81
                        get;
 
82
                        private set;
 
83
                }
 
84
 
 
85
                public CFSocketException (CFSocketError error)
 
86
                {
 
87
                        this.Error = error;
 
88
                }
 
89
        }
 
90
 
 
91
        struct CFSocketSignature {
 
92
                int protocolFamily;
 
93
                int socketType;
 
94
                int protocol;
 
95
                IntPtr address;
 
96
 
 
97
                public CFSocketSignature (AddressFamily family, SocketType type,
 
98
                                          ProtocolType proto, CFSocketAddress address)
 
99
                {
 
100
                        this.protocolFamily = AddressFamilyToInt (family);
 
101
                        this.socketType = SocketTypeToInt (type);
 
102
                        this.protocol = ProtocolToInt (proto);
 
103
                        this.address = address.Handle;
 
104
                }
 
105
 
 
106
                internal static int AddressFamilyToInt (AddressFamily family)
 
107
                {
 
108
                        switch (family) {
 
109
                        case AddressFamily.Unspecified:
 
110
                                return 0;
 
111
                        case AddressFamily.Unix:
 
112
                                return 1;
 
113
                        case AddressFamily.InterNetwork:
 
114
                                return 2;
 
115
                        case AddressFamily.AppleTalk:
 
116
                                return 16;
 
117
                        case AddressFamily.InterNetworkV6:
 
118
                                return 30;
 
119
                        default:
 
120
                                throw new ArgumentException ();
 
121
                        }
 
122
                }
 
123
 
 
124
                internal static int SocketTypeToInt (SocketType type)
 
125
                {
 
126
                        if ((int) type == 0)
 
127
                                return 0;
 
128
 
 
129
                        switch (type) {
 
130
                        case SocketType.Unknown:
 
131
                                return 0;
 
132
                        case SocketType.Stream:
 
133
                                return 1;
 
134
                        case SocketType.Dgram:
 
135
                                return 2;
 
136
                        case SocketType.Raw:
 
137
                                return 3;
 
138
                        case SocketType.Rdm:
 
139
                                return 4;
 
140
                        case SocketType.Seqpacket:
 
141
                                return 5;
 
142
                        default:
 
143
                                throw new ArgumentException ();
 
144
                        }
 
145
                }
 
146
 
 
147
                internal static int ProtocolToInt (ProtocolType type)
 
148
                {
 
149
                        return (int) type;
 
150
                }
 
151
 
 
152
        }
 
153
 
 
154
        class CFSocketAddress : CFDataBuffer {
 
155
                public CFSocketAddress (IPEndPoint endpoint)
 
156
                        : base (CreateData (endpoint))
 
157
                {
 
158
                }
 
159
 
 
160
                internal static IPEndPoint EndPointFromAddressPtr (IntPtr address)
 
161
                {
 
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);
 
173
                                } else {
 
174
                                        throw new ArgumentException ();
 
175
                                }
 
176
                        }
 
177
                }
 
178
 
 
179
                static byte[] CreateData (IPEndPoint endpoint)
 
180
                {
 
181
                        if (endpoint.AddressFamily == AddressFamily.InterNetwork) {
 
182
                                var buffer = new byte [16];
 
183
                                buffer [0] = 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);
 
188
                                return buffer;
 
189
                        } else if (endpoint.AddressFamily == AddressFamily.InterNetworkV6) {
 
190
                                var buffer = new byte [28];
 
191
                                buffer [0] = 32;
 
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);
 
196
                                return buffer;
 
197
                        } else {
 
198
                                throw new ArgumentException ();
 
199
                        }
 
200
                }
 
201
        }
 
202
 
 
203
        public class CFSocket : CFType, INativeObject, IDisposable {
 
204
                IntPtr handle;
 
205
                GCHandle gch;
 
206
 
 
207
                ~CFSocket ()
 
208
                {
 
209
                        Dispose (false);
 
210
                }
 
211
                
 
212
                public void Dispose ()
 
213
                {
 
214
                        Dispose (true);
 
215
                        GC.SuppressFinalize (this);
 
216
                }
 
217
 
 
218
                public IntPtr Handle {
 
219
                        get { return handle; }
 
220
                }
 
221
                
 
222
                protected virtual void Dispose (bool disposing)
 
223
                {
 
224
                        if (disposing) {
 
225
                                if (gch.IsAllocated)
 
226
                                        gch.Free ();
 
227
                        }
 
228
                        if (handle != IntPtr.Zero) {
 
229
                                CFObject.CFRelease (handle);
 
230
                                handle = IntPtr.Zero;
 
231
                        }
 
232
                }
 
233
 
 
234
                delegate void CFSocketCallBack (IntPtr s, int type, IntPtr address, IntPtr data, IntPtr info);
 
235
 
 
236
                [MonoPInvokeCallback (typeof(CFSocketCallBack))]
 
237
                static void OnCallback (IntPtr s, int type, IntPtr address, IntPtr data, IntPtr info)
 
238
                {
 
239
                        var socket = GCHandle.FromIntPtr (info).Target as CFSocket;
 
240
                        CFSocketCallBackType cbType = (CFSocketCallBackType)type;
 
241
 
 
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;
 
250
                                else
 
251
                                        result = (CFSocketError)Marshal.ReadInt32 (data);
 
252
                                socket.OnConnect (new CFSocketConnectEventArgs (result));
 
253
                        }
 
254
                }
 
255
 
 
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);
 
260
 
 
261
                [DllImport (Constants.CoreFoundationLibrary)]
 
262
                extern static IntPtr CFSocketCreateWithNative (IntPtr allocator, CFSocketNativeHandle sock,
 
263
                                                               CFSocketCallBackType callBackTypes,
 
264
                                                               CFSocketCallBack callout, IntPtr ctx);
 
265
 
 
266
                [DllImport (Constants.CoreFoundationLibrary)]
 
267
                extern static IntPtr CFSocketCreateRunLoopSource (IntPtr allocator, IntPtr socket, int order);
 
268
 
 
269
                public CFSocket ()
 
270
                        : this (0, 0, 0)
 
271
                {
 
272
                }
 
273
 
 
274
                public CFSocket (AddressFamily family, SocketType type, ProtocolType proto)
 
275
                        : this (family, type, proto, CFRunLoop.Current)
 
276
                {
 
277
                }
 
278
 
 
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)
 
283
                {
 
284
                }
 
285
 
 
286
                CFSocket (int family, int type, int proto, CFRunLoop loop)
 
287
                {
 
288
                        var cbTypes = CFSocketCallBackType.DataCallBack | CFSocketCallBackType.ConnectCallBack;
 
289
 
 
290
                        gch = GCHandle.Alloc (this);
 
291
                        var ctx = new CFStreamClientContext ();
 
292
                        ctx.Info = GCHandle.ToIntPtr (gch);
 
293
 
 
294
                        var ptr = Marshal.AllocHGlobal (Marshal.SizeOf (typeof(CFStreamClientContext)));
 
295
                        try {
 
296
                                Marshal.StructureToPtr (ctx, ptr, false);
 
297
                                handle = CFSocketCreate (
 
298
                                        IntPtr.Zero, family, type, proto, cbTypes, OnCallback, ptr);
 
299
                        } finally {
 
300
                                Marshal.FreeHGlobal (ptr);
 
301
                        }
 
302
 
 
303
                        if (handle == IntPtr.Zero)
 
304
                                throw new CFSocketException (CFSocketError.Error);
 
305
                        gch = GCHandle.Alloc (this);
 
306
 
 
307
                        var source = new CFRunLoopSource (CFSocketCreateRunLoopSource (IntPtr.Zero, handle, 0));
 
308
                        loop.AddSource (source, CFRunLoop.CFDefaultRunLoopMode);
 
309
                }
 
310
 
 
311
                internal CFSocket (CFSocketNativeHandle sock)
 
312
                {
 
313
                        var cbTypes = CFSocketCallBackType.DataCallBack | CFSocketCallBackType.WriteCallBack;
 
314
 
 
315
                        gch = GCHandle.Alloc (this);
 
316
                        var ctx = new CFStreamClientContext ();
 
317
                        ctx.Info = GCHandle.ToIntPtr (gch);
 
318
 
 
319
                        var ptr = Marshal.AllocHGlobal (Marshal.SizeOf (typeof(CFStreamClientContext)));
 
320
                        try {
 
321
                                Marshal.StructureToPtr (ctx, ptr, false);
 
322
                                handle = CFSocketCreateWithNative (
 
323
                                        IntPtr.Zero, sock, cbTypes, OnCallback, ptr);
 
324
                        } finally {
 
325
                                Marshal.FreeHGlobal (ptr);
 
326
                        }
 
327
 
 
328
                        if (handle == IntPtr.Zero)
 
329
                                throw new CFSocketException (CFSocketError.Error);
 
330
 
 
331
                        var source = new CFRunLoopSource (CFSocketCreateRunLoopSource (IntPtr.Zero, handle, 0));
 
332
                        var loop = CFRunLoop.Current;
 
333
                        loop.AddSource (source, CFRunLoop.CFDefaultRunLoopMode);
 
334
                }
 
335
 
 
336
                CFSocket (IntPtr handle)
 
337
                {
 
338
                        this.handle = handle;
 
339
                        gch = GCHandle.Alloc (this);
 
340
 
 
341
                        var source = new CFRunLoopSource (CFSocketCreateRunLoopSource (IntPtr.Zero, handle, 0));
 
342
                        var loop = CFRunLoop.Current;
 
343
                        loop.AddSource (source, CFRunLoop.CFDefaultRunLoopMode);
 
344
                }
 
345
 
 
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);
 
351
 
 
352
                public static CFSocket CreateConnectedToSocketSignature (AddressFamily family, SocketType type,
 
353
                                                                         ProtocolType proto, IPEndPoint endpoint,
 
354
                                                                         double timeout)
 
355
                {
 
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);
 
363
 
 
364
                                return new CFSocket (handle);
 
365
                        }
 
366
                }
 
367
 
 
368
                [DllImport (Constants.CoreFoundationLibrary)]
 
369
                extern static CFSocketNativeHandle CFSocketGetNative (IntPtr handle);
 
370
 
 
371
                internal CFSocketNativeHandle GetNative ()
 
372
                {
 
373
                        return CFSocketGetNative (handle);
 
374
                }
 
375
 
 
376
                [DllImport (Constants.CoreFoundationLibrary)]
 
377
                extern static CFSocketError CFSocketSetAddress (IntPtr handle, IntPtr address);
 
378
 
 
379
                public void SetAddress (IPAddress address, int port)
 
380
                {
 
381
                        SetAddress (new IPEndPoint (address, port));
 
382
                }
 
383
 
 
384
                public void SetAddress (IPEndPoint endpoint)
 
385
                {
 
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);
 
394
                        }
 
395
                }
 
396
 
 
397
                [DllImport (Constants.CoreFoundationLibrary)]
 
398
                extern static CFSocketFlags CFSocketGetSocketFlags (IntPtr handle);
 
399
 
 
400
                public CFSocketFlags GetSocketFlags ()
 
401
                {
 
402
                        return CFSocketGetSocketFlags (handle);
 
403
                }
 
404
 
 
405
                [DllImport (Constants.CoreFoundationLibrary)]
 
406
                extern static void CFSocketSetSocketFlags (IntPtr handle, CFSocketFlags flags);
 
407
 
 
408
                public void SetSocketFlags (CFSocketFlags flags)
 
409
                {
 
410
                        CFSocketSetSocketFlags (handle, flags);
 
411
                }
 
412
 
 
413
                [DllImport (Constants.CoreFoundationLibrary)]
 
414
                extern static void CFSocketDisableCallBacks (IntPtr handle, CFSocketCallBackType types);
 
415
 
 
416
                public void DisableCallBacks (CFSocketCallBackType types)
 
417
                {
 
418
                        CFSocketDisableCallBacks (handle, types);
 
419
                }
 
420
 
 
421
                [DllImport (Constants.CoreFoundationLibrary)]
 
422
                extern static void CFSocketEnableCallBacks (IntPtr handle, CFSocketCallBackType types);
 
423
 
 
424
                public void EnableCallBacks (CFSocketCallBackType types)
 
425
                {
 
426
                        CFSocketEnableCallBacks (handle, types);
 
427
                }
 
428
 
 
429
                [DllImport (Constants.CoreFoundationLibrary)]
 
430
                extern static CFSocketError CFSocketSendData (IntPtr handle, IntPtr address, IntPtr data, double timeout);
 
431
 
 
432
                public void SendData (byte[] data, double timeout)
 
433
                {
 
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);
 
438
                        }
 
439
                }
 
440
 
 
441
                public class CFSocketAcceptEventArgs : EventArgs {
 
442
                        internal CFSocketNativeHandle SocketHandle {
 
443
                                get;
 
444
                                private set;
 
445
                        }
 
446
 
 
447
                        public IPEndPoint RemoteEndPoint {
 
448
                                get;
 
449
                                private set;
 
450
                        }
 
451
 
 
452
                        public CFSocketAcceptEventArgs (CFSocketNativeHandle handle, IPEndPoint remote)
 
453
                        {
 
454
                                this.SocketHandle = handle;
 
455
                                this.RemoteEndPoint = remote;
 
456
                        }
 
457
 
 
458
                        public CFSocket CreateSocket ()
 
459
                        {
 
460
                                return new CFSocket (SocketHandle);
 
461
                        }
 
462
 
 
463
                        public override string ToString ()
 
464
                        {
 
465
                                return string.Format ("[CFSocketAcceptEventArgs: RemoteEndPoint={0}]", RemoteEndPoint);
 
466
                        }
 
467
                }
 
468
 
 
469
                public class CFSocketConnectEventArgs : EventArgs {
 
470
                        public CFSocketError Result {
 
471
                                get;
 
472
                                private set;
 
473
                        }
 
474
 
 
475
                        public CFSocketConnectEventArgs (CFSocketError result)
 
476
                        {
 
477
                                this.Result = result;
 
478
                        }
 
479
 
 
480
                        public override string ToString ()
 
481
                        {
 
482
                                return string.Format ("[CFSocketConnectEventArgs: Result={0}]", Result);
 
483
                        }
 
484
                }
 
485
 
 
486
                public event EventHandler<CFSocketAcceptEventArgs> AcceptEvent;
 
487
                public event EventHandler<CFSocketConnectEventArgs> ConnectEvent;
 
488
 
 
489
                void OnAccepted (CFSocketAcceptEventArgs args)
 
490
                {
 
491
                        if (AcceptEvent != null)
 
492
                                AcceptEvent (this, args);
 
493
                }
 
494
 
 
495
                void OnConnect (CFSocketConnectEventArgs args)
 
496
                {
 
497
                        if (ConnectEvent != null)
 
498
                                ConnectEvent (this, args);
 
499
                }
 
500
 
 
501
                [DllImport (Constants.CoreFoundationLibrary)]
 
502
                extern static CFSocketError CFSocketConnectToAddress (IntPtr handle, IntPtr address, double timeout);
 
503
 
 
504
                public void Connect (IPAddress address, int port, double timeout)
 
505
                {
 
506
                        Connect (new IPEndPoint (address, port), timeout);
 
507
                }
 
508
 
 
509
                public void Connect (IPEndPoint endpoint, double timeout)
 
510
                {
 
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);
 
515
                        }
 
516
                }
 
517
        }
 
518
}