Cmdlets in C#

Powershell v2 makes accessing Class Libraries written in C# very simple. Import-Module allows you to reference DLL’s directly and System.Management.Automation (which is available on Package Manager) allows you to expose your classes to Powershell. Powershell even handles complex types without having to write any additional layers (like serialization).

    [Cmdlet(VerbsCommon.Get, "RespondToMe")]
    public class Get_SomeInformation : PSCmdlet
    {
        [Parameter(Position=0, Mandatory=true)]
        public    string    Name;

        protected override void ProcessRecord()
        {
            this.WriteObject(new MyClass()    {
                Text = String.Format("Hello {0}!", Name)
                });
        }
    }

    public class MyClass
    {
        public  DateTime    When            = DateTime.Now;
        public  string      Text;
        public  bool        ItWorkedGreat   = true;
    }

Verbs of course define the prefix used when calling the CmdLet, in this case it will end up as Get-RespondToMe. Parameter positions come into play when you don’t specify parameter names, you can also take input from the pipeline via the attribute ValueFromPipeline.

WriteObject sends data back down the pipeline, which as demonstrated here, can be pretty much anything you wish. Lets see what we get…

PS> Import-Module .\cmdlets_in_csharp.dll
PS> Get-RespondToMe -Name Mike

When                    Text                            ItWorkedGreat
----                    ----                            -------------
7/25/2013 4:02:27 PM    Hello Mike!                              True

PS> (Get-RespondToMe -Name Mike).Text
Hello Mike!

This example is available on subversion:

svn checkout http://www.gotinker.com/svn/cmdlets_in_csharp

Securing IPv6 with IPSec, protecting yourself in an end-to-end world!

With IPv6 there is no more NAT. All your machines have end-to-end connections to their destination. It’s taken for granted the implied security that NAT provides, by preventing incoming connections. There’s also no longer a need to use a VPN, if I want to connect to my work PC I merely use it’s publicly available IPv6 address.

So what we need is a good (default deny) firewall policy to start with, but then we have the problem of encryption without using a VPN. This is where IPSec comes in, providing end-to-end encryption at the packet level. That way we can then happily use normally unencrypted protocols securely. We can also harden already encrypted protocols (SSH, RDP).

First thing we need to do is install “racoon” and “ipsec-tools” on both machines we wish to securely communicate between, in my case my Lubuntu 11.10 laptop and a Ubuntu 11.04 Server VPS. These two machines have the following IPv6 addresses (anonymized):

Laptop 2604:xxxx:xxx::500
VPS 2607:xxxx:xxx::f49

To install the relevant packages on Debian based systems:

#sudo apt-get install ipsec-tools racoon

Then I need to edit “/etc/ipsec-tools.conf” to tell each machine I expect IPSec to be used to/from a certain IPv6 address, on my laptop it looks like this:

So any IP on my laptop (::0/0) to my VPS outgoing, then my VPS to any IP on my laptop (::0/0) incoming. Then same on the VPS but switching the IP:

#!/usr/sbin/setkey -f
flush;
spdflush;

spdadd ::0/0 2604:xxxx:xxx::500 any -P out ipsec esp/transport//require ah/transport//require;
spdadd 2604:xxxx:xxx::500 ::0/0 any -P in ipsec esp/transport//require ah/transport//require;

Then we need to configure the level of encryption and key exchange we use on both machines. These settings must match exactly or key exchange and encryption will fail. These settings are defined in “/etc/racoon/racoon.conf”. The man pages have information on the algorithms you can use, in my case I’ve gone for “uber paranoid” settings like this:

# Author: Mike Lovell
# Modified: 2011-08-16

path pre_shared_key "/home/mikelovell/.ipsec/config/keys.txt";

remote anonymous
{
  exchange_mode main;
  lifetime time 12 hour;

  proposal
  {
    encryption_algorithm aes;
    hash_algorithm sha512;
    authentication_method pre_shared_key;
    dh_group modp4096;
  }
}

sainfo anonymous
{
  pfs_group modp4096;
  lifetime time 12 hour;
  encryption_algorithm rijndael, aes;
  authentication_algorithm hmac_sha512;
  compression_algorithm deflate;
}

I store my IPSec keys inside “/home”, which is encrypted (mounted on boot) on my machines – You’re probably best keeping your keys in “/etc/racoon/keys.txt”. In here you need to put the destination IP along with a password to use to communicate – To generate the password I always use “apg -a 1 -m 128″, here’s what the file might look like on the laptop:

2607:xxxx:xxx::f49 not_a_good_password!

And on the VPS:

2604:xxxx:xxx::500 not_a_good_password!

All that’s left to do now is restart the IPSec service and racoon, make sure you have another way into your machine (not via IPv6) before you do this – If something doesn’t work you don’t want to be locked out!

#sudo /etc/init.d/setkey restart
#sudo /etc/init.d/racoon restart

Once you’ve done that on both machines as soon as you establish a new connection (anything, try a ping6) you should see IPSec successfully negotiate in your syslog:

initiate new phase 1 negotiation: 2604:xxxx:xxx::500[500]<=>2607:xxxx:xxx::f49[500]
begin Identity Protection mode.
received Vendor ID: DPD
ISAKMP-SA established 2604:xxxx:xxx::500[500]-2607:xxxx:xxx::f49[500] spi:d____________f
initiate new phase 2 negotiation: 2604:xxxx:xxx::500[500]<=>2607:xxxx:xxx::f49[500]
IPsec-SA established: AH/Transport 2604:xxxx:xxx::500[500]->2607:xxxx:xxx::f49[500] spi=11___2(0x6____)
IPsec-SA established: ESP/Transport 2604:xxxx:xxx::500[500]->2607:xxxx:xxx::f49[500] spi=1___1(0xc____)
IPsec-SA established: AH/Transport 2604:xxxx:xxx::500[500]->2607:xxxx:xxx::f49[500] spi=1___5(0xb____)
IPsec-SA established: ESP/Transport 2604:xxxx:xxx::500[500]->2607:xxxx:xxx::f49[500] spi=96___3(0x5____)

Viola! You care confirm if you like by sniffing the network traffic via “wireshark”.

IPv6 without native support from your provider, via SiXS

You’ll need a Linux machine or device supporting AICCU and ip6tables to act as IPv6 router.  It doesn’t have to be the same machine/device as IPv4 router.  If you’re using DD-WRT on your IPv4 router then there’s a tutorial covering this here.

First you need to pop over to www.sixxs.net and sign up for a account (it’s free).  Once that’s approved you’ll need to request a tunnel.

For safeties sake, as some connectivity providers block other protocols, select “Dynamic NAT-traversing IPv4 Endpoint using AYIYA” as your endpoint.

Then you’ll need to select a PoP that’s close to you (to keep latency low) and give a reason why you want an IPv6 tunnel (use your imagination!)

After that’s approved you’ll need to request a subnet

Now we just need to setup our IPv6 router (in my case a Debian VM).  First lets setup AICCU which will configure a tunnel device for IPv6 with SixXS.  During the ncurses installer, you will be asked for your SixXS username and password:

# apt-get install aiccu

An “ifconfig” should now show the tunnel to be present:

sixxs     Link encap:UNSPEC  HWaddr 00-00-00-00-00-00-00-00-00-00-00-00-00-00-00-00
  inet6 addr: fe80::xxxx:xxx:xx:2/64 Scope:Link
  inet6 addr: 2604:xxxx:xxx:xx::2/64 Scope:Global
  UP POINTOPOINT RUNNING NOARP MULTICAST  MTU:1280  Metric:1
  RX packets:1989470 errors:0 dropped:0 overruns:0 frame:0
  TX packets:1187540 errors:0 dropped:0 overruns:0 carrier:0
  collisions:0 txqueuelen:500
  RX bytes:1401336999 (1.3 GiB)  TX bytes:306453776 (292.2 MiB)

Then we need to enable IPv6 forwarding (as root):

#echo 1 > /proc/sys/net/ipv6/conf/all/forwarding

To make this persistent you’ll need to add/ammend this rule in “/etc/sysctl.conf”:

net.ipv6.conf.default.forwarding=1

Lets now setup an IPv6 address from subnet we’ve been given to act as our IPv6 gateway, in my case my subnet is (anonymized):

2604:xxxx:xxx::/48

So in “/etc/network/interfaces” I put the following:

iface eth0 inet6 static
  address 2604:xxxx:xxx::1
  netmask 48

Now whenever the network comes up I want to add some routes in to make sure my IPv6 traffic is directed correctly, to do this I created a script called “/etc/network/if-up.d/ipv6routes” and marked it as executable:

#!/bin/sh

ip -6 route add 2604:8800:112::1/64 dev eth0
ip -6 route add 2604:8800:112::/48 dev lo

Then we just need to setup the ip6tables rules to forward the traffic correctly, in my case:

ip6tables -P FORWARD DROP

ip6tables -A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT
ip6tables -A OUTPUT -m state --state RELATED,ESTABLISHED -j ACCEPT

ip6tables -A INPUT -i lo -j ACCEPT
ip6tables -A OUTPUT -o lo -j ACCEPT

ip6tables -A FORWARD -i eth0 -s 2604:xxxx:xxx::/48 -j ACCEPT
ip6tables -A FORWARD -i sixxs -o eth0 -d 2604:xxxx:xxx::/48 -j ACCEPT

To make these persistent you’ll need to set them to be loaded when the network comes up, what I usually do is this:

ip6tables-save > /etc/iptables-ipv6.conf

Then create a script called “/etc/network/if-up.d/iptables” and mark it as executable with the following inside it:

#!/bin/sh

ip6tables-restore < /etc/iptables-ipv6.conf

Now we assign an IPv6 address to a different machine in the network and set the gateway as the address we setup previously (2604:xxxx:xxx::1), here’s what the “ifconfig” looks like:

wlan0     Link encap:Ethernet  HWaddr 00:23:14:53:56:3c
  inet addr:192.168.x.xxx  Bcast:192.168.x.xxx  Mask:255.255.x.x
  inet6 addr: 2604:xxxx:xxx::500/64 Scope:Global
  inet6 addr: fe80::xxx:xxxx:xxxx:xxxx/64 Scope:Link
  UP BROADCAST RUNNING MULTICAST  MTU:1280  Metric:1
  RX packets:403511 errors:0 dropped:0 overruns:0 frame:0
  TX packets:366141 errors:0 dropped:0 overruns:0 carrier:0
  collisions:0 txqueuelen:1000
  RX bytes:195242106 (195.2 MB)  TX bytes:60409846 (60.4 MB)

And hopefully, it all works!

# traceroute6 -n www.sixxs.net
traceroute to nginx.sixxs.net (2001:1af8:4050::2) from 2604:xxxx:xxx::500, 30 hops max, 24 byte
 1  2604:xxxx:xxx::1  2.871 ms  2.892 ms  9.606 ms
 2  2604:xxxx:xxx:31::1  137.17 ms  150.576 ms  168.446 ms
 3  2620:0:6b0:a::1  204.796 ms  245.039 ms  329.039 ms
 4  2001:1900:2100::171  326.815 ms  256.593 ms  385.289 ms
   ...                         ...

Extracting music from flash videos using mencoder and mplayer

If you’ve ever downloaded music videos from a website (for example via “youtube-dl”) you may have been in the position of wanting to extract the music in “MP3″ format from the “FLV” (flash video) file, here’s how you can do this:

#mencoder source.flv -ovc frameno -oac mp3lame -lameopts br=128 -noskip -o resampled.avi
#mplayer -dumpaudio -dumpfile final.mp3 resampled.avi

Both “mencoder” and  “mplayer” can be used on both Windows and Linux (in the latest version of Ubuntu both are available via the package manager).

X11 Forwarding of Pidgin with Sound using PulseAudio

With the aid of PulseAudio it’s possible to remotely host applications such as Pidgin and still have sound enabled (something that’s very handy with an IM client for notification purposes).

First, on the client and server, you’ll need to make sure PulseAudio is installed (it should be there by default in Ubuntu 10.10 if that’s your client), for the client you can install all you need like this:

#apt-get install pulseaudio pulseaudio-utils paprefs

And for the server:

#apt-get install pulseaudio pulseaudio-utils

Then on the client allow network access to the local sound devices (you can open the preferences by typing “papref” at console or going to “System | Preferences | PulseAudio Preferences”):

"paprefs" Dialog

(Security Note: The port being used (TCP/4713) is blocked by the firewall on this machine to anything but LocalHost, you should do the same)

Then, when we connect to the server, we want to enable X11 forwarding and setup a reverse tunnel to allow us to send sounds to the client, we do this like so:

#ssh -X R 4713:127.0.0.1:4713

Start up Pidgin:

#pidgin &

Then change the sound preferences to use “paplay” to play the sounds down the reverse tunnel:

Pidgin Sound Dialog

And there we go, job done.

I like to have Pidgin running on a virtual machine at home to prevent any monitoring of my IM communications when at work, on-site or using an untrusted network.

Hybrid Cryptosystem

When using ‘asymmetric’ (public-private key) encryption, there are a few limitations:

  • Slow Speed - Symmetric (key) encryption is far quicker
  • Single Recipient – You can only encrypt to a single public key

However we can combine ‘asymmetric’ and ‘symmetric’ cryposystems to create a hybrid system which will allow us to harness the speed of ‘symmetric’encryption and allow multiple recipients by simply encrypting the shared ‘symmetric key’ to each public ‘asymmetric key’ we want to use.

So lets create a simple format for a ‘hybrid cryptosystem‘.  We’re going to be using ‘RSA‘ for our ‘asymmetric encryption‘, and AES (Rijndael) for our ‘symmetric encryption‘.

1 Byte    Recepient count: 1-255 recepients allowed
4 Bytes   Recepient 1 data: Size (Int32)
20 Bytes  Recepient 1 data: SHA1 hash of public key
n Bytes   Recepient 1 data: Shared key/password (encrypted to public key)
...
4 Bytes   Recepient n data: Size (Int32)
20 Bytes  Recepient n data: SHA1 hash of public key
n Bytes   Recepient n data: Shared key/password (encrypted to public key)

n Bytes   Encrypted data: Encrypted using AES (Rijndael) and shared key/password

 

Now we have our format defined, we can implement a simple C# application that follows this.  First we’re going to implement a ‘CryptoProvider‘ class.  This is going to handle both ‘asymmetric‘ and ‘symmetric‘ encryption and decryption, key generation, hash generation and random byte generation.  Our key/password is going to be randomly generated and 48 bytes long.  The first 16 will be the ‘IV‘ vector, the last 32 will be the ‘key‘.

// Author: Mike Lovell (mike.lovell@gotinker.com)
 
class CryptoProvider
{
    private    RSACryptoServiceProvider    rsaProvider;
 
 
    public CryptoProvider(int keySize)
    {
        rsaProvider = new RSACryptoServiceProvider(keySize);    // Generate new key
    }
 
 
    public CryptoProvider(string xmlKey)
    {
        rsaProvider = new RSACryptoServiceProvider();
 
        rsaProvider.FromXmlString(xmlKey);    // Import XML Key
    }
 
 
    public string ToXmlString(bool includePrivateKey)
    {
        return rsaProvider.ToXmlString(includePrivateKey);    // Export XML Key
    }
 
 
    public byte[] Hash()
    {
        var sha1 = new SHA1Managed();
 
        // Hash from the modulus
        return sha1.ComputeHash(rsaProvider.ExportParameters(false).Modulus);
    }
 
 
    public byte[] Encrypt(byte[] data)
    {
        return rsaProvider.Encrypt(data, false);
    }
 
 
    public byte[] Decrypt(byte[] data)
    {
        return rsaProvider.Decrypt(data, false);
    }
 
 
    public static byte[] RandomSequence(int size)
    {
        var random        = new byte[size];
        var rngProvider   = new RNGCryptoServiceProvider();

        rngProvider.GetBytes(random);
 
        return random;
    }
 
 
    public static byte[] Encrypt(byte[] data, byte[] password)
    {
        var rijndael        = Rijndael.Create();
 
        rijndael.KeySize    = 256;    // Maximum key size
        rijndael.IV         = SubBytes(password, 0, 16);    // First 16 bytes is vector
        rijndael.Key        = TrimBytes(SubBytes(password, 16, password.Length - 16), 32);    // Last 32 is key
        rijndael.Mode       = CipherMode.CBC;
 
        var stream          = new MemoryStream();
        var cryptoStream    = new CryptoStream(stream, rijndael.CreateEncryptor(), CryptoStreamMode.Write);
 
        cryptoStream.Write(data, 0, data.Length);
        cryptoStream.FlushFinalBlock();
 
        cryptoStream.Close();
 
        var encryptedData = stream.ToArray();
 
        stream.Close();
 
        return encryptedData;
    }
 
 
    public static byte[] Decrypt(byte[] data, byte[] password)
    {
        var rijndael         = Rijndael.Create();
 
        rijndael.KeySize     = 256;    // Maximum key size
        rijndael.IV          = SubBytes(password, 0, 16);    // First 16 bytes is vector
        rijndael.Key         = TrimBytes(SubBytes(password, 16, password.Length - 16), 32);    // Last 32 is key
        rijndael.Mode        = CipherMode.CBC;
 
        var stream           = new MemoryStream(data);
        var cryptoStream     = new CryptoStream(stream, rijndael.CreateDecryptor(), CryptoStreamMode.Read);
 
        var    length        = cryptoStream.Read(data, 0, data.Length);
        var    decryptedData = new byte[length];
 
        stream.Read(decryptedData, 0, length);
 
        cryptoStream.Close();
        stream.Close();
 
        Buffer.BlockCopy(data, 0, decryptedData, 0, length);
 
        return decryptedData;
    }
 
 
    public byte[] DecryptPasswordAndData(byte[] data)
    {
        var    buffer          = new MemoryStream(data);
        var    keyCount        = buffer.ReadByte();    // First byte is key count
        var    thisHash        = Convert.ToBase64String(this.Hash());
        var    password        = (byte[])null;
 
        if (keyCount < 1) throw new Exception("No keys found");

        var    keyLengthBuffer = new byte[4];

        for (int i=0; i < keyCount; i++)
        {
            // First 4 bytes are Int32 of encrypted hash/password size
            buffer.Read(keyLengthBuffer, 0, 4);
 
            var    keyLength      = BitConverter.ToInt32(keyLengthBuffer, 0);
            var    keyHash        = new byte[20];    // First 20 bytes is SHA1 hash
            var    keyPassword    = new byte[keyLength - 20];    // Remaining byes are encrypted password
 
            buffer.Read(keyHash, 0, 20);    // Read first 20 bytes (SHA1 Hash)
            buffer.Read(keyPassword, 0, keyPassword.Length);    // Read remaining bytes (password)
 
            if (Convert.ToBase64String(keyHash) == thisHash)
            {
                password = Decrypt(keyPassword);    // Decrypt password
            }
        }
 
        if (password == null) throw new Exception("Data not encrypted to your key");
 
        // remaining bytes are encrypted data
        var encryptedData     = new byte[buffer.Length - buffer.Position];
 
        // Read remaining bytes
        buffer.Read(encryptedData, 0, encryptedData.Length);
 
        buffer.Close();
 
        return CryptoProvider.Decrypt(encryptedData, password);    // Decrypt data
    }
 
 
    private static byte[] SubBytes(byte[] source, int offset, int length)
    {
        var    subData    = new byte[length - offset];
 
        Buffer.BlockCopy(source, offset, subData, 0, length - offset);
 
        return subData;
    }
 
 
    private static byte[] TrimBytes(byte[] source, int maxLength)
    {
        if (source.Length > maxLength)
        {
            var trimmedData = new byte[maxLength];
 
            Buffer.BlockCopy(source, 0, trimmedData, 0, maxLength);
 
            return trimmedData;
        }
 
        return source;
    }
}

Then lets create a collection of ‘CryptoProvider‘ which will allow us to encrypt our data to multiple public keys.

class CryptoProviderCollection : List<CryptoProvider>
{
    public byte[] EncryptPasswordAndData(byte[] data)
    {
        if (this.Count < 1 || this.Count > 255) throw new Exception("Too few or too many recepients");
 
        var password    = CryptoProvider.RandomSequence(16 + 32);    // Random password
                                                                    // First 16 = Vector
                                                                    // Last 32 = Key
        var stream        = new MemoryStream();

        stream.WriteByte((byte)this.Count); // First byte is recepient count
 
        var encryptedPasswords    =
            (
                from    item
                in        this
                select    CombineBytes
                    (
                        item.Hash(),            // Public key hash
                        item.Encrypt(password)    // Password encrypted for recipient
                    )
            );
 
 
        foreach (var encryptedPassword in encryptedPasswords)
        {    // For each recepient
 
            // Store encrypted password length as Int32 (first 4 bytes)
            stream.Write(BitConverter.GetBytes(encryptedPassword.Length), 0, 4);
            // Store encrypted password
            stream.Write(encryptedPassword, 0,  encryptedPassword.Length);
        }
 
        var encryptedData = CryptoProvider.Encrypt(data, password);    // Encrypt data
 
        stream.Write(encryptedData, 0, encryptedData.Length);        // Store
 
        var encryptedBytes = stream.ToArray();
 
        stream.Close();
 
        return encryptedBytes;
    }
 
 
    private byte[] CombineBytes(byte[] dataA, byte[] dataB)
    {
        var combinedData = new byte[dataA.Length + dataB.Length];
 
        Buffer.BlockCopy(dataA, 0, combinedData, 0, dataA.Length);
        Buffer.BlockCopy(dataB, 0, combinedData, dataA.Length, dataB.Length);
 
        return combinedData;
    }
}

Now lets see if it works.  We’ll create 3 ‘RSA‘ keys then encrypt the data for the first two recipients ONLY, just to see if the 3rd cannot decrypt the data (which should be the case).

class Program
{
    static void Main(string[] args)
    {
        var    key1    = new CryptoProvider(1024);    // Make a new key
        var    key2    = new CryptoProvider(2048);    // ..
        var    key3    = new CryptoProvider(1024);    // ..
 
        var col    = new CryptoProviderCollection();

        col.Add(key1);    // Add only key1 and key2
        col.Add(key2);    // to the collection
 
        // Encrypt data and random password for key1 and key2
        var encryptedData    = col.EncryptPasswordAndData(Encoding.UTF8.GetBytes("Test Encrypted Data"));

        // Decrypt using key1
        Console.WriteLine(Encoding.UTF8.GetString(key1.DecryptPasswordAndData(encryptedData)));
        // Decrypt using key2
        Console.WriteLine(Encoding.UTF8.GetString(key2.DecryptPasswordAndData(encryptedData)));

        try
        {
            // Decrypt using key3, that we DIDN'T use for encryption, this will fail
            Console.WriteLine(Encoding.UTF8.GetString(key3.DecryptPasswordAndData(encryptedData)));
        }
        catch (Exception e)
        {
            Console.WriteLine("ERROR: {0}",  e.Message.ToString());
        }
 
        Console.ReadLine();
    }
}

And the result you should see is the following:

Test Encrypted Data
Test Encrypted Data
ERROR: Data not encrypted to your key

You can expand the classes to carry out ‘exception handling‘, and build a robust ‘hybrid cryptosystem‘ from this basic implementation.

Download Visual Studio 2010 Project (8.33k)

Audible Ping

This is either the greatest or most annoying tool ever!  I have to reboot a lot of machines and I always kick of a continuous ping against them to see them go down and come up again.  I thought it would be great if I could make a hospital heart-monitor style beeping ping utility, so I did.  Here it is.

First lets write a ‘ToneHandler‘ class to control our sound.  We’ll give this the capability to either make a single ‘beep‘ or a continous tone (indicating the machine is down).  We’re going to use ‘Console.Beep‘ for this.  We’ll run our continuous tone function in its own thread so we can terminate it when the machine starts pinging again.

// Author Mike Lovell (mike.lovell@gotinker.com)
 
class ToneHandler
{
    private    Thread    constantToneThread;
 
 
    public void SingleTone(int frequency, int duration)
    {
        Console.Beep(frequency, duration);
    }
 
 
    public void ConstantToneBegin()
    {
        if (constantToneThread != null) return;    // If already making tone, exit
 
        constantToneThread    = new Thread(new ThreadStart(delegate()
            {
                while (true)
                {    // Infinite loop, we'll kill it later
                    Console.Beep(500, Int32.MaxValue);
                }
            }));
 
        constantToneThread.IsBackground = true;
        constantToneThread.Start();    // Kick it off
    }
 
 
    public void ConstantToneEnd()
    {
        if (constantToneThread == null) return;    // If already not making tone, exit
 
        Console.Beep(500, 1);    // Send another Beep, this will terminte the other one
 
        constantToneThread.Join(500);    // Terminate thread
 
        constantToneThread = null;
 
        return;
    }
}

Now lets make a little wrapper for the .NET ‘Ping‘ class.  Because we’re going to link the tone and duration of the tone to the round-trip time of the ping, all we want our ping class to return is the time the ping took, or ‘-1‘ if the ping failed.

class PingWrapper
{
    private    IPAddress    ipAddress;
 
    private    Ping          ping          = new Ping();
    private    PingOptions   pingOptions   = new PingOptions();
 
    private    byte[]        buffer        = new byte[32]
        {    // 32 byte buffer
            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
            0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF
        };
 
    public PingWrapper(string host)
    {
        // Resolve the host once, not at every ping
        ipAddress = Dns.GetHostAddresses(host)[0];
 
        pingOptions.DontFragment = true;
    }
 
 
    public int Send()
    {
        var reply = ping.Send(ipAddress, 1000, buffer, pingOptions);
 
        // Only send a ping every 1 second
        Thread.Sleep(1000 - (int)reply.RoundtripTime);
 
        if (reply.Status == IPStatus.Success)
        {
            return (int)reply.RoundtripTime;
        }
        else
        {
            return -1;
        }
    }
}

Now we’ll take the first argument of the command line and use it as the host we’re going to ping.  We’ll setup a continuous loop in a different thread (so we can terminate it from the main thread) and get it to call our tone class based on the output of ping.  We’ll also display a 1 character status of what happened (success ‘_‘ or failed ‘!‘)

class Program
{
    static void Main(string[] args)
    {
        if (args.Length != 1)
        {
            Console.WriteLine("Usage: gotinker.audibleping <hostname or IP>");
            return;
        }
 
        var    terminate    = false;
        var    toneHandler  = new ToneHandler();
        var    ping         = new PingWrapper(args[0]);
 
        var loopThread    = new Thread(new ThreadStart(delegate()
            {
                while (true)
                {
                    if (terminate) break;
 
                    var result = ping.Send();
 
                    if (result == -1)
                    {
                        Console.Write("!");
                        toneHandler.ConstantToneBegin();
                    }
                    else
                    {
                        Console.Write("_");
                        toneHandler.ConstantToneEnd();
                        toneHandler.SingleTone(500 - (result / 4), ((result * 4) + 100));
                    }
                }
            }));
 
        Console.WriteLine("(Press any enter to exit)");
 
        loopThread.Start();
 
        Console.ReadLine();
 
        terminate = true;    // Our loops checks for this to exit
 
        while (loopThread.ThreadState == ThreadState.Running)
        {
            Thread.Sleep(50);    // Wait fot the thread to terminate
        }
    }
}

And the result, here’s me pinging something while I reboot it (Download Audio of Demo):

(Press any enter to exit)
________!!!!!!_________!_____________

Download Visual Studio 2010 Project (7.16k)

Handling Unhandled Exceptions

If an exception occurs outside a ‘try-catch block‘, it’s nice to have a way of either logging this, or having yourself notified.  The ‘EventLog‘ might be a way to go, but in the case of software running on your end-users PC, you might want to make it easy for them to send ‘unhandled exceptions‘ to you directly.  After all, if it’s complicated they may not have the technical expertise to get the information to you.  Something you might find easy might be beyond the reach of your end-users.

Here’s a quick example of both how to implement a ‘UnhandledException‘ EventHandler, and how to allow your end-user to easily email the information to you.

First lets make a little function that’s designed to fail!  This following function is going to always throw an Exception (divide by zero).

class Program
{
    // Author Mike Lovell (mike.lovell@gotinker.com)
 
    private static int OtherFunction(int i)
    {
        var j = 0;
 
        return i / j;    // Divide by zero exception
                        // will happen here
    }

Now the handler.  Inside ‘AppDomain.CurrentDomain‘ lives the ‘UnhandledException‘ event handler, this is fired when an exception is thrown (and of course, not handled!).  What we’re going to do is open up the end-users default mail client, with the ‘to‘, ‘subject‘ and ‘body‘ prefilled with the information we need.  In this case, we’re going to take the date, the full path and filename of the executable and the full exception (including stack trace).  Because we want the exception exactly as it would have appeared, and including any non-latin characters (their mail client may not support) we’ve Base64 encoded the UTF8 bytes of our exception string.

We can very easily reverse this process at the other end, to get to the exception.

    static void Main(string[] cargs)
    {
        AppDomain.CurrentDomain.UnhandledException += delegate(object sender, UnhandledExceptionEventArgs args)
            {
                var exception = ((Exception)args.ExceptionObject);

                var mailProcess = Process.Start
                    (
                        String.Format
                            (
                                "mailto:null@gotinker.com?subject=Unhandled Exception ({0})&body={1}%0A{2}%0A%0A{3}",
                                HttpUtility.HtmlEncode(exception.Message),
                                DateTime.UtcNow.ToString("yyyy-MM-dd hh:mm:ss"),
                                Process.GetCurrentProcess().MainModule.FileName,
                                Convert.ToBase64String(Encoding.UTF8.GetBytes(exception.ToString())
                            )
                        )
                    );
            };

Now we want to call the function so we actually cause the exception to be thrown

        OtherFunction(123);
    }
}

And the result?  Should look something like this:

GoTinker Exception Handler Email

Exception Handler Email

Download Visual Studio 2010 Project (6.78k)

Inheriting EventArgs for better events

In a lot of cases, you need to return some custom data from your events.  Inheriting from ‘EventArgs’ gives you the opportunity to do this.  Here’s an example of how you do it.  Fortunately this time, it’s all self explanatory.

// Author Mike Lovell (mike.lovell@gotinker.com)
 
class Firey
{
    public class MyEventArgs : EventArgs
    {
        public    bool    IsSomethingNice;
        public    int        SomeNumber;
        public    string    SomeText;
    }
 
 
    public    event    EventHandler<MyEventArgs>    MyEvent;
 
 
    public void FireEvent()
    {
        if (MyEvent != null) MyEvent(this, new MyEventArgs()
            {
                IsSomethingNice  = true,
                SomeNumber       = 666,
                SomeText         = "Hello World"
            });
    }
}
 
 
 
class Program
{
    static void Main(string[] pargs)
    {
        var firey    = new Firey();
 
        firey.MyEvent += delegate(object sender, Firey.MyEventArgs args)
            {
                Console.WriteLine
                    (
                        "Event Fired:nIsSomethingNice: {0}nSomeNumber: {1}nSomeText: {2}",
                        args.IsSomethingNice,
                        args.SomeNumber,
                        args.SomeText
                    );
            };
 
        firey.FireEvent();
 
        Console.ReadLine();
    }
}

Download Visual Studio 2010 Project (6.35k)