Merge pull request #409 from Alkarex/patch-1
[mono.git] / mcs / class / System / System.Net.NetworkInformation / Ping.cs
index 4f7f7b7b998d1dec0ccc58a39c56a97efa6156ef..1299b60798b24ab5bed2e2afc323d2f5497e40ac 100644 (file)
@@ -26,7 +26,6 @@
 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
 //
-#if NET_2_0
 using System;
 using System.IO;
 using System.Text;
@@ -35,7 +34,12 @@ using System.Globalization;
 using System.ComponentModel;
 using System.Net.Sockets;
 using System.Security.Principal;
+using System.Security.Cryptography;
 using System.Runtime.InteropServices;
+#if NET_4_5
+using System.Threading;
+using System.Threading.Tasks;
+#endif
 
 namespace System.Net.NetworkInformation {
        [MonoTODO ("IPv6 support is missing")]
@@ -60,11 +64,14 @@ namespace System.Net.NetworkInformation {
                static readonly string [] PingBinPaths = new string [] {
                        "/bin/ping",
                        "/sbin/ping",
-                       "/usr/sbin/ping"
+                       "/usr/sbin/ping",
+#if MONODROID
+                       "/system/bin/ping"
+#endif
                };
                static readonly string PingBinPath;
                const int default_timeout = 4000; // 4 sec.
-               const int identifier = 1; // no need to be const, but there's no place to change it.
+               ushort identifier;
 
                // This value is correct as of Linux kernel version 2.6.25.9
                // See /usr/include/linux/capability.h
@@ -76,6 +83,9 @@ namespace System.Net.NetworkInformation {
 
                BackgroundWorker worker;
                object user_async_state;
+#if NET_4_5
+               CancellationTokenSource cts;
+#endif
                
                public event PingCompletedEventHandler PingCompleted;
                
@@ -103,6 +113,11 @@ namespace System.Net.NetworkInformation {
                
                public Ping ()
                {
+                       // Generate a new random 16 bit identifier for every ping
+                       RNGCryptoServiceProvider rng = new RNGCryptoServiceProvider ();
+                       byte [] randomIdentifier = new byte [2];
+                       rng.GetBytes (randomIdentifier);
+                       identifier = (ushort)(randomIdentifier [0] + (randomIdentifier [1] << 8));
                }
   
                [DllImport ("libc", EntryPoint="capget")]
@@ -138,10 +153,14 @@ namespace System.Net.NetworkInformation {
 
                protected void OnPingCompleted (PingCompletedEventArgs e)
                {
-                       if (PingCompleted != null)
-                               PingCompleted (this, e);
                        user_async_state = null;
                        worker = null;
+#if NET_4_5
+                       cts = null;
+#endif
+
+                       if (PingCompleted != null)
+                               PingCompleted (this, e);
                }
 
                // Sync
@@ -292,9 +311,8 @@ namespace System.Net.NetworkInformation {
                                ping.Start ();
 
 #pragma warning disable 219
-                       // No need to read stdout or stderr as long as the output is less than 4k on linux <= 2.6.11 and 65k after that
-                       //      string stdout = ping.StandardOutput.ReadToEnd ();
-                       //      string stderr = ping.StandardError.ReadToEnd ();
+                               string stdout = ping.StandardOutput.ReadToEnd ();
+                               string stderr = ping.StandardError.ReadToEnd ();
 #pragma warning restore 219
                                
                                trip_time = (long) (DateTime.Now - sentTime).TotalMilliseconds;
@@ -356,8 +374,13 @@ namespace System.Net.NetworkInformation {
 
                public void SendAsync (IPAddress address, int timeout, byte [] buffer, PingOptions options, object userToken)
                {
+#if NET_4_5
+                       if ((worker != null) || (cts != null))
+                               throw new InvalidOperationException ("Another SendAsync operation is in progress");
+#else
                        if (worker != null)
                                throw new InvalidOperationException ("Another SendAsync operation is in progress");
+#endif
 
                        worker = new BackgroundWorker ();
                        worker.DoWork += delegate (object o, DoWorkEventArgs ea) {
@@ -380,6 +403,13 @@ namespace System.Net.NetworkInformation {
 
                public void SendAsyncCancel ()
                {
+#if NET_4_5
+                       if (cts != null) {
+                               cts.Cancel ();
+                               return;
+                       }
+#endif
+
                        if (worker == null)
                                throw new InvalidOperationException ("SendAsync operation is not in progress");
                        worker.CancelAsync ();
@@ -399,7 +429,7 @@ namespace System.Net.NetworkInformation {
                        }
 
                        // to be sent
-                       public IcmpMessage (byte type, byte code, short identifier, short sequence, byte [] data)
+                       public IcmpMessage (byte type, byte code, ushort identifier, ushort sequence, byte [] data)
                        {
                                bytes = new byte [data.Length + 8];
                                bytes [0] = type;
@@ -423,18 +453,18 @@ namespace System.Net.NetworkInformation {
                                get { return bytes [1]; }
                        }
 
-                       public byte Identifier {
-                               get { return (byte) (bytes [4] + (bytes [5] << 8)); }
+                       public ushort Identifier {
+                               get { return (ushort) (bytes [4] + (bytes [5] << 8)); }
                        }
 
-                       public byte Sequence {
-                               get { return (byte) (bytes [6] + (bytes [7] << 8)); }
+                       public ushort Sequence {
+                               get { return (ushort) (bytes [6] + (bytes [7] << 8)); }
                        }
 
                        public byte [] Data {
                                get {
                                        byte [] data = new byte [bytes.Length - 8];
-                                       Buffer.BlockCopy (bytes, 0, data, 0, data.Length);
+                                       Buffer.BlockCopy (bytes, 8, data, 0, data.Length);
                                        return data;
                                }
                        }
@@ -504,28 +534,78 @@ namespace System.Net.NetworkInformation {
                        CultureInfo culture = CultureInfo.InvariantCulture;
                        StringBuilder args = new StringBuilder ();
                        uint t = Convert.ToUInt32 (Math.Floor ((timeout + 1000) / 1000.0));
-#if NET_2_0
                        bool is_mac = ((int) Environment.OSVersion.Platform == 6);
                        if (!is_mac)
-#endif
                                args.AppendFormat (culture, "-q -n -c {0} -w {1} -t {2} -M ", DefaultCount, t, options.Ttl);
-#if NET_2_0
                        else
                                args.AppendFormat (culture, "-q -n -c {0} -t {1} -o -m {2} ", DefaultCount, t, options.Ttl);
                        if (!is_mac)
-#endif
                                args.Append (options.DontFragment ? "do " : "dont ");
-#if NET_2_0
                        else if (options.DontFragment)
                                args.Append ("-D ");
-#endif
 
                        args.Append (address.ToString ());
 
                        return args.ToString ();
                }
 
+#if NET_4_5
+               public Task<PingReply> SendPingAsync (IPAddress address, int timeout, byte [] buffer)
+               {
+                       return SendPingAsync (address, default_timeout, default_buffer, new PingOptions ());
+               }
+
+               public Task<PingReply> SendPingAsync (IPAddress address, int timeout)
+               {
+                       return SendPingAsync (address, default_timeout, default_buffer);
+               }
+
+               public Task<PingReply> SendPingAsync (IPAddress address)
+               {
+                       return SendPingAsync (address, default_timeout);
+               }
+
+               public Task<PingReply> SendPingAsync (string hostNameOrAddress, int timeout, byte [] buffer)
+               {
+                       return SendPingAsync (hostNameOrAddress, timeout, buffer, new PingOptions ());
+               }
+
+               public Task<PingReply> SendPingAsync (string hostNameOrAddress, int timeout, byte [] buffer, PingOptions options)
+               {
+                       IPAddress address = Dns.GetHostEntry (hostNameOrAddress).AddressList [0];
+                       return SendPingAsync (address, timeout, buffer, options);
+               }
+
+               public Task<PingReply> SendPingAsync (string hostNameOrAddress, int timeout)
+               {
+                       return SendPingAsync (hostNameOrAddress, timeout, default_buffer);
+               }
+
+               public Task<PingReply> SendPingAsync (string hostNameOrAddress)
+               {
+                       return SendPingAsync (hostNameOrAddress, default_timeout);
+               }
+
+               public Task<PingReply> SendPingAsync (IPAddress address, int timeout, byte [] buffer, PingOptions options)
+               {
+                       if ((worker != null) || (cts != null))
+                               throw new InvalidOperationException ("Another SendAsync operation is in progress");
+
+                       var task = Task<PingReply>.Factory.StartNew (
+                               () => Send (address, timeout, buffer, options), cts.Token);
+
+                       task.ContinueWith ((t) => {
+                               if (t.IsCanceled)
+                                       OnPingCompleted (new PingCompletedEventArgs (null, true, null, null));
+                               else if (t.IsFaulted)
+                                       OnPingCompleted (new PingCompletedEventArgs (t.Exception, false, null, null));
+                               else
+                                       OnPingCompleted (new PingCompletedEventArgs (null, false, null, t.Result));
+                       });
+
+                       return task;
+               }
+#endif
        }
 }
-#endif