PageRenderTime 26ms CodeModel.GetById 22ms RepoModel.GetById 1ms app.codeStats 0ms

/mcs/class/System/System.Net.NetworkInformation/NetworkChange.cs

https://github.com/pruiz/mono
C# | 506 lines | 390 code | 84 blank | 32 comment | 77 complexity | 291a83e6d0bf404a2d11168fd0cebc36 MD5 | raw file
Possible License(s): LGPL-2.0, MPL-2.0-no-copyleft-exception, CC-BY-SA-3.0, GPL-2.0
  1. //
  2. // System.Net.NetworkInformation.NetworkChange
  3. //
  4. // Authors:
  5. // Gonzalo Paniagua Javier (LinuxNetworkChange) (gonzalo@novell.com)
  6. // Aaron Bockover (MacNetworkChange) (abock@xamarin.com)
  7. //
  8. // Copyright (c) 2006,2011 Novell, Inc. (http://www.novell.com)
  9. // Copyright (c) 2013 Xamarin, Inc. (http://www.xamarin.com)
  10. //
  11. // Permission is hereby granted, free of charge, to any person obtaining
  12. // a copy of this software and associated documentation files (the
  13. // "Software"), to deal in the Software without restriction, including
  14. // without limitation the rights to use, copy, modify, merge, publish,
  15. // distribute, sublicense, and/or sell copies of the Software, and to
  16. // permit persons to whom the Software is furnished to do so, subject to
  17. // the following conditions:
  18. //
  19. // The above copyright notice and this permission notice shall be
  20. // included in all copies or substantial portions of the Software.
  21. //
  22. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  23. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  24. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  25. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  26. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  27. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  28. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  29. //
  30. using System;
  31. using System.Net.Sockets;
  32. using System.Runtime.InteropServices;
  33. using System.Threading;
  34. #if NETWORK_CHANGE_STANDALONE
  35. namespace NetworkInformation {
  36. public class NetworkAvailabilityEventArgs : EventArgs
  37. {
  38. public bool IsAvailable { get; set; }
  39. public NetworkAvailabilityEventArgs (bool available)
  40. {
  41. IsAvailable = available;
  42. }
  43. }
  44. public delegate void NetworkAddressChangedEventHandler (object sender, EventArgs args);
  45. public delegate void NetworkAvailabilityChangedEventHandler (object sender, NetworkAvailabilityEventArgs args);
  46. #else
  47. namespace System.Net.NetworkInformation {
  48. #endif
  49. internal interface INetworkChange : IDisposable {
  50. event NetworkAddressChangedEventHandler NetworkAddressChanged;
  51. event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged;
  52. bool HasRegisteredEvents { get; }
  53. }
  54. public sealed class NetworkChange {
  55. static INetworkChange networkChange;
  56. public static event NetworkAddressChangedEventHandler NetworkAddressChanged {
  57. add {
  58. lock (typeof (INetworkChange)) {
  59. MaybeCreate ();
  60. if (networkChange != null)
  61. networkChange.NetworkAddressChanged += value;
  62. }
  63. }
  64. remove {
  65. lock (typeof (INetworkChange)) {
  66. if (networkChange != null) {
  67. networkChange.NetworkAddressChanged -= value;
  68. MaybeDispose ();
  69. }
  70. }
  71. }
  72. }
  73. public static event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged {
  74. add {
  75. lock (typeof (INetworkChange)) {
  76. MaybeCreate ();
  77. if (networkChange != null)
  78. networkChange.NetworkAvailabilityChanged += value;
  79. }
  80. }
  81. remove {
  82. lock (typeof (INetworkChange)) {
  83. if (networkChange != null) {
  84. networkChange.NetworkAvailabilityChanged -= value;
  85. MaybeDispose ();
  86. }
  87. }
  88. }
  89. }
  90. static void MaybeCreate ()
  91. {
  92. #if MONOTOUCH_WATCH || ORBIS
  93. throw new PlatformNotSupportedException ("NetworkInformation.NetworkChange is not supported on the current platform.");
  94. #else
  95. if (networkChange != null)
  96. return;
  97. try {
  98. networkChange = new MacNetworkChange ();
  99. } catch {
  100. #if !NETWORK_CHANGE_STANDALONE && !MONOTOUCH
  101. networkChange = new LinuxNetworkChange ();
  102. #endif
  103. }
  104. #endif // MONOTOUCH_WATCH
  105. }
  106. static void MaybeDispose ()
  107. {
  108. if (networkChange != null && networkChange.HasRegisteredEvents) {
  109. networkChange.Dispose ();
  110. networkChange = null;
  111. }
  112. }
  113. }
  114. #if !MONOTOUCH_WATCH && !ORBIS
  115. internal sealed class MacNetworkChange : INetworkChange
  116. {
  117. const string DL_LIB = "/usr/lib/libSystem.dylib";
  118. const string CORE_SERVICES_LIB = "/System/Library/Frameworks/SystemConfiguration.framework/SystemConfiguration";
  119. const string CORE_FOUNDATION_LIB = "/System/Library/Frameworks/CoreFoundation.framework/CoreFoundation";
  120. [UnmanagedFunctionPointerAttribute (CallingConvention.Cdecl)]
  121. delegate void SCNetworkReachabilityCallback (IntPtr target, NetworkReachabilityFlags flags, IntPtr info);
  122. [DllImport (DL_LIB)]
  123. static extern IntPtr dlopen (string path, int mode);
  124. [DllImport (DL_LIB)]
  125. static extern IntPtr dlsym (IntPtr handle, string symbol);
  126. [DllImport (DL_LIB)]
  127. static extern int dlclose (IntPtr handle);
  128. [DllImport (CORE_FOUNDATION_LIB)]
  129. static extern void CFRelease (IntPtr handle);
  130. [DllImport (CORE_FOUNDATION_LIB)]
  131. static extern IntPtr CFRunLoopGetMain ();
  132. [DllImport (CORE_SERVICES_LIB)]
  133. static extern IntPtr SCNetworkReachabilityCreateWithAddress (IntPtr allocator, ref sockaddr_in sockaddr);
  134. [DllImport (CORE_SERVICES_LIB)]
  135. static extern bool SCNetworkReachabilityGetFlags (IntPtr reachability, out NetworkReachabilityFlags flags);
  136. [DllImport (CORE_SERVICES_LIB)]
  137. static extern bool SCNetworkReachabilitySetCallback (IntPtr reachability, SCNetworkReachabilityCallback callback, ref SCNetworkReachabilityContext context);
  138. [DllImport (CORE_SERVICES_LIB)]
  139. static extern bool SCNetworkReachabilityScheduleWithRunLoop (IntPtr reachability, IntPtr runLoop, IntPtr runLoopMode);
  140. [DllImport (CORE_SERVICES_LIB)]
  141. static extern bool SCNetworkReachabilityUnscheduleFromRunLoop (IntPtr reachability, IntPtr runLoop, IntPtr runLoopMode);
  142. [StructLayout (LayoutKind.Explicit, Size = 28)]
  143. struct sockaddr_in {
  144. [FieldOffset (0)] public byte sin_len;
  145. [FieldOffset (1)] public byte sin_family;
  146. public static sockaddr_in Create ()
  147. {
  148. return new sockaddr_in {
  149. sin_len = 28,
  150. sin_family = 2 // AF_INET
  151. };
  152. }
  153. }
  154. [StructLayout (LayoutKind.Sequential)]
  155. struct SCNetworkReachabilityContext {
  156. public IntPtr version;
  157. public IntPtr info;
  158. public IntPtr retain;
  159. public IntPtr release;
  160. public IntPtr copyDescription;
  161. }
  162. [Flags]
  163. enum NetworkReachabilityFlags {
  164. None = 0,
  165. TransientConnection = 1 << 0,
  166. Reachable = 1 << 1,
  167. ConnectionRequired = 1 << 2,
  168. ConnectionOnTraffic = 1 << 3,
  169. InterventionRequired = 1 << 4,
  170. ConnectionOnDemand = 1 << 5,
  171. IsLocalAddress = 1 << 16,
  172. IsDirect = 1 << 17,
  173. IsWWAN = 1 << 18,
  174. ConnectionAutomatic = ConnectionOnTraffic
  175. }
  176. IntPtr handle;
  177. IntPtr runLoopMode;
  178. SCNetworkReachabilityCallback callback;
  179. bool scheduledWithRunLoop;
  180. NetworkReachabilityFlags flags;
  181. event NetworkAddressChangedEventHandler networkAddressChanged;
  182. event NetworkAvailabilityChangedEventHandler networkAvailabilityChanged;
  183. public event NetworkAddressChangedEventHandler NetworkAddressChanged {
  184. add {
  185. value (null, EventArgs.Empty);
  186. networkAddressChanged += value;
  187. }
  188. remove { networkAddressChanged -= value; }
  189. }
  190. public event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged {
  191. add {
  192. value (null, new NetworkAvailabilityEventArgs (IsAvailable));
  193. networkAvailabilityChanged += value;
  194. }
  195. remove { networkAvailabilityChanged -= value; }
  196. }
  197. bool IsAvailable {
  198. get {
  199. return (flags & NetworkReachabilityFlags.Reachable) != 0 &&
  200. (flags & NetworkReachabilityFlags.ConnectionRequired) == 0;
  201. }
  202. }
  203. public bool HasRegisteredEvents {
  204. get { return networkAddressChanged != null || networkAvailabilityChanged != null; }
  205. }
  206. public MacNetworkChange ()
  207. {
  208. var sockaddr = sockaddr_in.Create ();
  209. handle = SCNetworkReachabilityCreateWithAddress (IntPtr.Zero, ref sockaddr);
  210. if (handle == IntPtr.Zero)
  211. throw new Exception ("SCNetworkReachabilityCreateWithAddress returned NULL");
  212. callback = new SCNetworkReachabilityCallback (HandleCallback);
  213. var info = new SCNetworkReachabilityContext {
  214. info = GCHandle.ToIntPtr (GCHandle.Alloc (this))
  215. };
  216. SCNetworkReachabilitySetCallback (handle, callback, ref info);
  217. scheduledWithRunLoop =
  218. LoadRunLoopMode () &&
  219. SCNetworkReachabilityScheduleWithRunLoop (handle, CFRunLoopGetMain (), runLoopMode);
  220. SCNetworkReachabilityGetFlags (handle, out flags);
  221. }
  222. bool LoadRunLoopMode ()
  223. {
  224. var cfLibHandle = dlopen (CORE_FOUNDATION_LIB, 0);
  225. if (cfLibHandle == IntPtr.Zero)
  226. return false;
  227. try {
  228. runLoopMode = dlsym (cfLibHandle, "kCFRunLoopDefaultMode");
  229. if (runLoopMode != IntPtr.Zero) {
  230. runLoopMode = Marshal.ReadIntPtr (runLoopMode);
  231. return runLoopMode != IntPtr.Zero;
  232. }
  233. } finally {
  234. dlclose (cfLibHandle);
  235. }
  236. return false;
  237. }
  238. public void Dispose ()
  239. {
  240. lock (this) {
  241. if (handle == IntPtr.Zero)
  242. return;
  243. if (scheduledWithRunLoop)
  244. SCNetworkReachabilityUnscheduleFromRunLoop (handle, CFRunLoopGetMain (), runLoopMode);
  245. CFRelease (handle);
  246. handle = IntPtr.Zero;
  247. callback = null;
  248. flags = NetworkReachabilityFlags.None;
  249. scheduledWithRunLoop = false;
  250. }
  251. }
  252. [Mono.Util.MonoPInvokeCallback (typeof (SCNetworkReachabilityCallback))]
  253. static void HandleCallback (IntPtr reachability, NetworkReachabilityFlags flags, IntPtr info)
  254. {
  255. if (info == IntPtr.Zero)
  256. return;
  257. var instance = GCHandle.FromIntPtr (info).Target as MacNetworkChange;
  258. if (instance == null || instance.flags == flags)
  259. return;
  260. instance.flags = flags;
  261. var addressChanged = instance.networkAddressChanged;
  262. if (addressChanged != null)
  263. addressChanged (null, EventArgs.Empty);
  264. var availabilityChanged = instance.networkAvailabilityChanged;
  265. if (availabilityChanged != null)
  266. availabilityChanged (null, new NetworkAvailabilityEventArgs (instance.IsAvailable));
  267. }
  268. }
  269. #endif // !MONOTOUCH_WATCH
  270. #if !NETWORK_CHANGE_STANDALONE && !MONOTOUCH && !ORBIS
  271. internal sealed class LinuxNetworkChange : INetworkChange {
  272. [Flags]
  273. enum EventType {
  274. Availability = 1 << 0,
  275. Address = 1 << 1,
  276. }
  277. object _lock = new object ();
  278. Socket nl_sock;
  279. SocketAsyncEventArgs nl_args;
  280. EventType pending_events;
  281. Timer timer;
  282. NetworkAddressChangedEventHandler AddressChanged;
  283. NetworkAvailabilityChangedEventHandler AvailabilityChanged;
  284. public event NetworkAddressChangedEventHandler NetworkAddressChanged {
  285. add { Register (value); }
  286. remove { Unregister (value); }
  287. }
  288. public event NetworkAvailabilityChangedEventHandler NetworkAvailabilityChanged {
  289. add { Register (value); }
  290. remove { Unregister (value); }
  291. }
  292. public bool HasRegisteredEvents {
  293. get { return AddressChanged != null || AvailabilityChanged != null; }
  294. }
  295. public void Dispose ()
  296. {
  297. }
  298. //internal Socket (AddressFamily family, SocketType type, ProtocolType proto, IntPtr sock)
  299. bool EnsureSocket ()
  300. {
  301. lock (_lock) {
  302. if (nl_sock != null)
  303. return true;
  304. IntPtr fd = CreateNLSocket ();
  305. if (fd.ToInt64 () == -1)
  306. return false;
  307. var safeHandle = new SafeSocketHandle (fd, true);
  308. nl_sock = new Socket (0, SocketType.Raw, ProtocolType.Udp, safeHandle);
  309. nl_args = new SocketAsyncEventArgs ();
  310. nl_args.SetBuffer (new byte [8192], 0, 8192);
  311. nl_args.Completed += OnDataAvailable;
  312. nl_sock.ReceiveAsync (nl_args);
  313. }
  314. return true;
  315. }
  316. // _lock is held by the caller
  317. void MaybeCloseSocket ()
  318. {
  319. if (nl_sock == null || AvailabilityChanged != null || AddressChanged != null)
  320. return;
  321. CloseNLSocket (nl_sock.Handle);
  322. GC.SuppressFinalize (nl_sock);
  323. nl_sock = null;
  324. nl_args = null;
  325. }
  326. bool GetAvailability ()
  327. {
  328. NetworkInterface [] adapters = NetworkInterface.GetAllNetworkInterfaces ();
  329. foreach (NetworkInterface n in adapters) {
  330. // TODO: also check for a default route present?
  331. if (n.NetworkInterfaceType == NetworkInterfaceType.Loopback)
  332. continue;
  333. if (n.OperationalStatus == OperationalStatus.Up)
  334. return true;
  335. }
  336. return false;
  337. }
  338. void OnAvailabilityChanged (object unused)
  339. {
  340. NetworkAvailabilityChangedEventHandler d = AvailabilityChanged;
  341. if (d != null)
  342. d (null, new NetworkAvailabilityEventArgs (GetAvailability ()));
  343. }
  344. void OnAddressChanged (object unused)
  345. {
  346. NetworkAddressChangedEventHandler d = AddressChanged;
  347. if (d != null)
  348. d (null, EventArgs.Empty);
  349. }
  350. void OnEventDue (object unused)
  351. {
  352. EventType evts;
  353. lock (_lock) {
  354. evts = pending_events;
  355. pending_events = 0;
  356. timer.Change (-1, -1);
  357. }
  358. if ((evts & EventType.Availability) != 0)
  359. ThreadPool.QueueUserWorkItem (OnAvailabilityChanged);
  360. if ((evts & EventType.Address) != 0)
  361. ThreadPool.QueueUserWorkItem (OnAddressChanged);
  362. }
  363. void QueueEvent (EventType type)
  364. {
  365. lock (_lock) {
  366. if (timer == null)
  367. timer = new Timer (OnEventDue);
  368. if (pending_events == 0)
  369. timer.Change (150, -1);
  370. pending_events |= type;
  371. }
  372. }
  373. unsafe void OnDataAvailable (object sender, SocketAsyncEventArgs args)
  374. {
  375. if (nl_sock == null) // Recent changes in Mono cause MaybeCloseSocket to be called before OnDataAvailable
  376. return;
  377. EventType type;
  378. fixed (byte *ptr = args.Buffer) {
  379. type = ReadEvents (nl_sock.Handle, new IntPtr (ptr), args.BytesTransferred, 8192);
  380. }
  381. nl_sock.ReceiveAsync (nl_args);
  382. if (type != 0)
  383. QueueEvent (type);
  384. }
  385. void Register (NetworkAddressChangedEventHandler d)
  386. {
  387. EnsureSocket ();
  388. AddressChanged += d;
  389. }
  390. void Register (NetworkAvailabilityChangedEventHandler d)
  391. {
  392. EnsureSocket ();
  393. AvailabilityChanged += d;
  394. }
  395. void Unregister (NetworkAddressChangedEventHandler d)
  396. {
  397. lock (_lock) {
  398. AddressChanged -= d;
  399. MaybeCloseSocket ();
  400. }
  401. }
  402. void Unregister (NetworkAvailabilityChangedEventHandler d)
  403. {
  404. lock (_lock) {
  405. AvailabilityChanged -= d;
  406. MaybeCloseSocket ();
  407. }
  408. }
  409. #if MONOTOUCH || MONODROID
  410. const string LIBNAME = "__Internal";
  411. #else
  412. const string LIBNAME = "MonoPosixHelper";
  413. #endif
  414. [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
  415. static extern IntPtr CreateNLSocket ();
  416. [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
  417. static extern EventType ReadEvents (IntPtr sock, IntPtr buffer, int count, int size);
  418. [DllImport (LIBNAME, CallingConvention=CallingConvention.Cdecl)]
  419. static extern IntPtr CloseNLSocket (IntPtr sock);
  420. }
  421. #endif
  422. }