PageRenderTime 46ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/src/CoreFoundation/CFSocket.cs

https://github.com/kjpou1/maccore
C# | 518 lines | 412 code | 78 blank | 28 comment | 43 complexity | 59a1e2048e5b1d46e18191812ea5f0be MD5 | raw file
Possible License(s): Apache-2.0
  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. using System;
  30. using System.Net;
  31. using System.Net.Sockets;
  32. using System.Runtime.InteropServices;
  33. using MonoMac.CoreFoundation;
  34. using MonoMac.ObjCRuntime;
  35. namespace MonoMac.CoreFoundation {
  36. [Flags]
  37. public enum CFSocketCallBackType {
  38. NoCallBack = 0,
  39. ReadCallBack = 1,
  40. AcceptCallBack = 2,
  41. DataCallBack = 3,
  42. ConnectCallBack = 4,
  43. WriteCallBack = 8
  44. }
  45. public enum CFSocketError {
  46. Success = 0,
  47. Error = -1,
  48. Timeout = -2
  49. }
  50. [Flags]
  51. public enum CFSocketFlags {
  52. AutomaticallyReenableReadCallBack = 1,
  53. AutomaticallyReenableAcceptCallBack = 2,
  54. AutomaticallyReenableDataCallBack = 3,
  55. AutomaticallyReenableWriteCallBack = 8,
  56. LeaveErrors = 64,
  57. CloseOnInvalidate = 128
  58. }
  59. public struct CFSocketNativeHandle {
  60. internal readonly int handle;
  61. internal CFSocketNativeHandle (int handle)
  62. {
  63. this.handle = handle;
  64. }
  65. public override string ToString ()
  66. {
  67. return string.Format ("[CFSocketNativeHandle {0}]", handle);
  68. }
  69. }
  70. public class CFSocketException : Exception {
  71. public CFSocketError Error {
  72. get;
  73. private set;
  74. }
  75. public CFSocketException (CFSocketError error)
  76. {
  77. this.Error = error;
  78. }
  79. }
  80. struct CFSocketSignature {
  81. int protocolFamily;
  82. int socketType;
  83. int protocol;
  84. IntPtr address;
  85. public CFSocketSignature (AddressFamily family, SocketType type,
  86. ProtocolType proto, CFSocketAddress address)
  87. {
  88. this.protocolFamily = AddressFamilyToInt (family);
  89. this.socketType = SocketTypeToInt (type);
  90. this.protocol = ProtocolToInt (proto);
  91. this.address = address.Handle;
  92. }
  93. internal static int AddressFamilyToInt (AddressFamily family)
  94. {
  95. switch (family) {
  96. case AddressFamily.Unspecified:
  97. return 0;
  98. case AddressFamily.Unix:
  99. return 1;
  100. case AddressFamily.InterNetwork:
  101. return 2;
  102. case AddressFamily.AppleTalk:
  103. return 16;
  104. case AddressFamily.InterNetworkV6:
  105. return 30;
  106. default:
  107. throw new ArgumentException ();
  108. }
  109. }
  110. internal static int SocketTypeToInt (SocketType type)
  111. {
  112. if ((int) type == 0)
  113. return 0;
  114. switch (type) {
  115. case SocketType.Unknown:
  116. return 0;
  117. case SocketType.Stream:
  118. return 1;
  119. case SocketType.Dgram:
  120. return 2;
  121. case SocketType.Raw:
  122. return 3;
  123. case SocketType.Rdm:
  124. return 4;
  125. case SocketType.Seqpacket:
  126. return 5;
  127. default:
  128. throw new ArgumentException ();
  129. }
  130. }
  131. internal static int ProtocolToInt (ProtocolType type)
  132. {
  133. return (int) type;
  134. }
  135. }
  136. class CFSocketAddress : CFDataBuffer {
  137. public CFSocketAddress (IPEndPoint endpoint)
  138. : base (CreateData (endpoint))
  139. {
  140. }
  141. internal static IPEndPoint EndPointFromAddressPtr (IntPtr address)
  142. {
  143. using (var buffer = new CFDataBuffer (address)) {
  144. if (buffer [1] == 30) { // AF_INET6
  145. int port = (buffer [2] << 8) + buffer [3];
  146. var bytes = new byte [16];
  147. Buffer.BlockCopy (buffer.Data, 8, bytes, 0, 16);
  148. return new IPEndPoint (new IPAddress (bytes), port);
  149. } else if (buffer [1] == 2) { // AF_INET
  150. int port = (buffer [2] << 8) + buffer [3];
  151. var bytes = new byte [4];
  152. Buffer.BlockCopy (buffer.Data, 4, bytes, 0, 4);
  153. return new IPEndPoint (new IPAddress (bytes), port);
  154. } else {
  155. throw new ArgumentException ();
  156. }
  157. }
  158. }
  159. static byte[] CreateData (IPEndPoint endpoint)
  160. {
  161. if (endpoint.AddressFamily == AddressFamily.InterNetwork) {
  162. var buffer = new byte [16];
  163. buffer [0] = 16;
  164. buffer [1] = 2; // AF_INET
  165. buffer [2] = (byte)(endpoint.Port >> 8);
  166. buffer [3] = (byte)(endpoint.Port & 0xff);
  167. Buffer.BlockCopy (endpoint.Address.GetAddressBytes (), 0, buffer, 4, 4);
  168. return buffer;
  169. } else if (endpoint.AddressFamily == AddressFamily.InterNetworkV6) {
  170. var buffer = new byte [28];
  171. buffer [0] = 32;
  172. buffer [1] = 30; // AF_INET6
  173. buffer [2] = (byte)(endpoint.Port >> 8);
  174. buffer [3] = (byte)(endpoint.Port & 0xff);
  175. Buffer.BlockCopy (endpoint.Address.GetAddressBytes (), 0, buffer, 8, 16);
  176. return buffer;
  177. } else {
  178. throw new ArgumentException ();
  179. }
  180. }
  181. }
  182. public class CFSocket : CFType, INativeObject, IDisposable {
  183. IntPtr handle;
  184. GCHandle gch;
  185. ~CFSocket ()
  186. {
  187. Dispose (false);
  188. }
  189. public void Dispose ()
  190. {
  191. Dispose (true);
  192. GC.SuppressFinalize (this);
  193. }
  194. public IntPtr Handle {
  195. get { return handle; }
  196. }
  197. protected virtual void Dispose (bool disposing)
  198. {
  199. if (disposing) {
  200. if (gch.IsAllocated)
  201. gch.Free ();
  202. }
  203. if (handle != IntPtr.Zero) {
  204. CFObject.CFRelease (handle);
  205. handle = IntPtr.Zero;
  206. }
  207. }
  208. delegate void CFSocketCallBack (IntPtr s, int type, IntPtr address, IntPtr data, IntPtr info);
  209. [MonoPInvokeCallback (typeof(CFSocketCallBack))]
  210. static void OnCallback (IntPtr s, int type, IntPtr address, IntPtr data, IntPtr info)
  211. {
  212. var socket = GCHandle.FromIntPtr (info).Target as CFSocket;
  213. CFSocketCallBackType cbType = (CFSocketCallBackType)type;
  214. if (cbType == CFSocketCallBackType.AcceptCallBack) {
  215. var ep = CFSocketAddress.EndPointFromAddressPtr (address);
  216. var handle = new CFSocketNativeHandle (Marshal.ReadInt32 (data));
  217. socket.OnAccepted (new CFSocketAcceptEventArgs (handle, ep));
  218. } else if (cbType == CFSocketCallBackType.ConnectCallBack) {
  219. CFSocketError result;
  220. if (data == IntPtr.Zero)
  221. result = CFSocketError.Success;
  222. else
  223. result = (CFSocketError)Marshal.ReadInt32 (data);
  224. socket.OnConnect (new CFSocketConnectEventArgs (result));
  225. }
  226. }
  227. [DllImport (Constants.CoreFoundationLibrary)]
  228. extern static IntPtr CFSocketCreate (IntPtr allocator, int family, int type, int proto,
  229. CFSocketCallBackType callBackTypes,
  230. CFSocketCallBack callout, IntPtr ctx);
  231. [DllImport (Constants.CoreFoundationLibrary)]
  232. extern static IntPtr CFSocketCreateWithNative (IntPtr allocator, CFSocketNativeHandle sock,
  233. CFSocketCallBackType callBackTypes,
  234. CFSocketCallBack callout, IntPtr ctx);
  235. [DllImport (Constants.CoreFoundationLibrary)]
  236. extern static IntPtr CFSocketCreateRunLoopSource (IntPtr allocator, IntPtr socket, int order);
  237. public CFSocket ()
  238. : this (0, 0, 0)
  239. {
  240. }
  241. public CFSocket (AddressFamily family, SocketType type, ProtocolType proto)
  242. : this (family, type, proto, CFRunLoop.Current)
  243. {
  244. }
  245. public CFSocket (AddressFamily family, SocketType type, ProtocolType proto, CFRunLoop loop)
  246. : this (CFSocketSignature.AddressFamilyToInt (family),
  247. CFSocketSignature.SocketTypeToInt (type),
  248. CFSocketSignature.ProtocolToInt (proto), loop)
  249. {
  250. }
  251. CFSocket (int family, int type, int proto, CFRunLoop loop)
  252. {
  253. var cbTypes = CFSocketCallBackType.DataCallBack | CFSocketCallBackType.ConnectCallBack;
  254. gch = GCHandle.Alloc (this);
  255. var ctx = new CFStreamClientContext ();
  256. ctx.Info = GCHandle.ToIntPtr (gch);
  257. var ptr = Marshal.AllocHGlobal (Marshal.SizeOf (typeof(CFStreamClientContext)));
  258. try {
  259. Marshal.StructureToPtr (ctx, ptr, false);
  260. handle = CFSocketCreate (
  261. IntPtr.Zero, family, type, proto, cbTypes, OnCallback, ptr);
  262. } finally {
  263. Marshal.FreeHGlobal (ptr);
  264. }
  265. if (handle == IntPtr.Zero)
  266. throw new CFSocketException (CFSocketError.Error);
  267. gch = GCHandle.Alloc (this);
  268. var source = new CFRunLoopSource (CFSocketCreateRunLoopSource (IntPtr.Zero, handle, 0));
  269. loop.AddSource (source, CFRunLoop.CFDefaultRunLoopMode);
  270. }
  271. internal CFSocket (CFSocketNativeHandle sock)
  272. {
  273. var cbTypes = CFSocketCallBackType.DataCallBack | CFSocketCallBackType.WriteCallBack;
  274. gch = GCHandle.Alloc (this);
  275. var ctx = new CFStreamClientContext ();
  276. ctx.Info = GCHandle.ToIntPtr (gch);
  277. var ptr = Marshal.AllocHGlobal (Marshal.SizeOf (typeof(CFStreamClientContext)));
  278. try {
  279. Marshal.StructureToPtr (ctx, ptr, false);
  280. handle = CFSocketCreateWithNative (
  281. IntPtr.Zero, sock, cbTypes, OnCallback, ptr);
  282. } finally {
  283. Marshal.FreeHGlobal (ptr);
  284. }
  285. if (handle == IntPtr.Zero)
  286. throw new CFSocketException (CFSocketError.Error);
  287. var source = new CFRunLoopSource (CFSocketCreateRunLoopSource (IntPtr.Zero, handle, 0));
  288. var loop = CFRunLoop.Current;
  289. loop.AddSource (source, CFRunLoop.CFDefaultRunLoopMode);
  290. }
  291. CFSocket (IntPtr handle)
  292. {
  293. this.handle = handle;
  294. gch = GCHandle.Alloc (this);
  295. var source = new CFRunLoopSource (CFSocketCreateRunLoopSource (IntPtr.Zero, handle, 0));
  296. var loop = CFRunLoop.Current;
  297. loop.AddSource (source, CFRunLoop.CFDefaultRunLoopMode);
  298. }
  299. [DllImport (Constants.CoreFoundationLibrary)]
  300. extern static IntPtr CFSocketCreateConnectedToSocketSignature (IntPtr allocator, ref CFSocketSignature signature,
  301. CFSocketCallBackType callBackTypes,
  302. CFSocketCallBack callout,
  303. IntPtr context, double timeout);
  304. public static CFSocket CreateConnectedToSocketSignature (AddressFamily family, SocketType type,
  305. ProtocolType proto, IPEndPoint endpoint,
  306. double timeout)
  307. {
  308. var cbTypes = CFSocketCallBackType.ConnectCallBack | CFSocketCallBackType.DataCallBack;
  309. using (var address = new CFSocketAddress (endpoint)) {
  310. var sig = new CFSocketSignature (family, type, proto, address);
  311. var handle = CFSocketCreateConnectedToSocketSignature (
  312. IntPtr.Zero, ref sig, cbTypes, OnCallback, IntPtr.Zero, timeout);
  313. if (handle == IntPtr.Zero)
  314. throw new CFSocketException (CFSocketError.Error);
  315. return new CFSocket (handle);
  316. }
  317. }
  318. [DllImport (Constants.CoreFoundationLibrary)]
  319. extern static CFSocketNativeHandle CFSocketGetNative (IntPtr handle);
  320. internal CFSocketNativeHandle GetNative ()
  321. {
  322. return CFSocketGetNative (handle);
  323. }
  324. [DllImport (Constants.CoreFoundationLibrary)]
  325. extern static CFSocketError CFSocketSetAddress (IntPtr handle, IntPtr address);
  326. public void SetAddress (IPAddress address, int port)
  327. {
  328. SetAddress (new IPEndPoint (address, port));
  329. }
  330. public void SetAddress (IPEndPoint endpoint)
  331. {
  332. EnableCallBacks (CFSocketCallBackType.AcceptCallBack);
  333. var flags = GetSocketFlags ();
  334. flags |= CFSocketFlags.AutomaticallyReenableAcceptCallBack;
  335. SetSocketFlags (flags);
  336. using (var address = new CFSocketAddress (endpoint)) {
  337. var error = CFSocketSetAddress (handle, address.Handle);
  338. if (error != CFSocketError.Success)
  339. throw new CFSocketException (error);
  340. }
  341. }
  342. [DllImport (Constants.CoreFoundationLibrary)]
  343. extern static CFSocketFlags CFSocketGetSocketFlags (IntPtr handle);
  344. public CFSocketFlags GetSocketFlags ()
  345. {
  346. return CFSocketGetSocketFlags (handle);
  347. }
  348. [DllImport (Constants.CoreFoundationLibrary)]
  349. extern static void CFSocketSetSocketFlags (IntPtr handle, CFSocketFlags flags);
  350. public void SetSocketFlags (CFSocketFlags flags)
  351. {
  352. CFSocketSetSocketFlags (handle, flags);
  353. }
  354. [DllImport (Constants.CoreFoundationLibrary)]
  355. extern static void CFSocketDisableCallBacks (IntPtr handle, CFSocketCallBackType types);
  356. public void DisableCallBacks (CFSocketCallBackType types)
  357. {
  358. CFSocketDisableCallBacks (handle, types);
  359. }
  360. [DllImport (Constants.CoreFoundationLibrary)]
  361. extern static void CFSocketEnableCallBacks (IntPtr handle, CFSocketCallBackType types);
  362. public void EnableCallBacks (CFSocketCallBackType types)
  363. {
  364. CFSocketEnableCallBacks (handle, types);
  365. }
  366. [DllImport (Constants.CoreFoundationLibrary)]
  367. extern static CFSocketError CFSocketSendData (IntPtr handle, IntPtr address, IntPtr data, double timeout);
  368. public void SendData (byte[] data, double timeout)
  369. {
  370. using (var buffer = new CFDataBuffer (data)) {
  371. var error = CFSocketSendData (handle, IntPtr.Zero, buffer.Handle, timeout);
  372. if (error != CFSocketError.Success)
  373. throw new CFSocketException (error);
  374. }
  375. }
  376. public class CFSocketAcceptEventArgs : EventArgs {
  377. internal CFSocketNativeHandle SocketHandle {
  378. get;
  379. private set;
  380. }
  381. public IPEndPoint RemoteEndPoint {
  382. get;
  383. private set;
  384. }
  385. public CFSocketAcceptEventArgs (CFSocketNativeHandle handle, IPEndPoint remote)
  386. {
  387. this.SocketHandle = handle;
  388. this.RemoteEndPoint = remote;
  389. }
  390. public CFSocket CreateSocket ()
  391. {
  392. return new CFSocket (SocketHandle);
  393. }
  394. public override string ToString ()
  395. {
  396. return string.Format ("[CFSocketAcceptEventArgs: RemoteEndPoint={0}]", RemoteEndPoint);
  397. }
  398. }
  399. public class CFSocketConnectEventArgs : EventArgs {
  400. public CFSocketError Result {
  401. get;
  402. private set;
  403. }
  404. public CFSocketConnectEventArgs (CFSocketError result)
  405. {
  406. this.Result = result;
  407. }
  408. public override string ToString ()
  409. {
  410. return string.Format ("[CFSocketConnectEventArgs: Result={0}]", Result);
  411. }
  412. }
  413. public event EventHandler<CFSocketAcceptEventArgs> AcceptEvent;
  414. public event EventHandler<CFSocketConnectEventArgs> ConnectEvent;
  415. void OnAccepted (CFSocketAcceptEventArgs args)
  416. {
  417. if (AcceptEvent != null)
  418. AcceptEvent (this, args);
  419. }
  420. void OnConnect (CFSocketConnectEventArgs args)
  421. {
  422. if (ConnectEvent != null)
  423. ConnectEvent (this, args);
  424. }
  425. [DllImport (Constants.CoreFoundationLibrary)]
  426. extern static CFSocketError CFSocketConnectToAddress (IntPtr handle, IntPtr address, double timeout);
  427. public void Connect (IPAddress address, int port, double timeout)
  428. {
  429. Connect (new IPEndPoint (address, port), timeout);
  430. }
  431. public void Connect (IPEndPoint endpoint, double timeout)
  432. {
  433. using (var address = new CFSocketAddress (endpoint)) {
  434. var error = CFSocketConnectToAddress (handle, address.Handle, timeout);
  435. if (error != CFSocketError.Success)
  436. throw new CFSocketException (error);
  437. }
  438. }
  439. }
  440. }