using System;
using System.Drawing;
using System.Collections;
using System.ComponentModel;
using System.Windows.Forms;
using System.Threading;
using System.Net.Sockets;
using System.Net;
using System.Text;

namespace NetSniff
{
	/// <summary>
	/// Podsumowanie dla Form1.
	/// </summary>
	public class frmMain : System.Windows.Forms.Form
	{
		private System.Windows.Forms.Button btnStartSniff;
		private System.Windows.Forms.ListView lstDisplay;
		private System.Windows.Forms.ColumnHeader clhMachineName;
		private System.Windows.Forms.ColumnHeader clhBytesSent;
		private System.Net.Sockets.Socket listenerSocket;		
		private static bool listen = false;
		private static SortedList table = new SortedList();
		private static SortedList lastTable = null;
		private static Hashtable dnsLookupTable = new Hashtable();
		private System.Windows.Forms.Button btnStop;
		private System.Windows.Forms.Timer tmrUpdateDisplay;
		private System.Windows.Forms.Label label1;
		private System.Windows.Forms.Label lblTotal;
		private System.Windows.Forms.Label lblStartTime;
		private System.Windows.Forms.Label label3;
		private System.ComponentModel.IContainer components;

		public frmMain()
		{
			//
			// Wymagane do obsługi Windows Form Designer
			//
			InitializeComponent();
			InitializeSocket();

			//
			// TODO: Dodanie kodu konstruktora po wywołaniu InitializeComponent
			//
		}
		
		private void InitializeSocket()
		{
			try
			{
				listenerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Raw, ProtocolType.IP);				
				IPEndPoint localEP = new IPEndPoint(Dns.GetHostByName(Dns.GetHostName()).AddressList[0], 80);
				this.listenerSocket.Bind(localEP);
				// SIO_RCVALL
				// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/hh/winsock/wsapiref_6ik2.asp 
				// Ustawia gniazdo na odbiór wszystkich pakietów IP z siec. Uchwyt gniazda
				// przekazany do funkcji WSAIoctl musi należeć do rodziny adresów AF_INET, typu gniazda SOCK_RAW
				// oraz protokołu IPPROTO_IP. Gniazdo musi być także powiązane z lokalną
				// kartą sieciową, co oznacza brak możliwości wiązania z  INADDR_ANY. 
				// Po powiązaniu gniazda i ustawieniu ioctl wywołania do funkcji WSARecv lub recv
				// zwracane są datagramy IP przesyłane poprzez określony interfehs. Należy pamiętać
				// o utworzeniu odpowiednio dużego buforu. Ustawienie tego ioctl wymaga praw administratora
				// na komputerze lokalnym. SIO_RCVALL jest dostępny w systemie Windows 2000 lub nowszym.
				int code = unchecked((int)(0x80000000 | 0x18000000 | 1));
				byte[] inBuf = new byte[4];
				inBuf[0] = 1;
				byte[] outBuf = new byte[4];
				listenerSocket.IOControl(code, inBuf, outBuf);			
			}
			catch(Exception ex)
			{
				MessageBox.Show(ex.ToString());
			}
		}

		/// <summary>
		/// Oczyszczenie używanych zasobów.
		/// </summary>
		protected override void Dispose( bool disposing )
		{
			if( disposing )
			{
				if (components != null) 
				{
					components.Dispose();
					listen = false;
				}
			}
			base.Dispose( disposing );
		}

		#region Windows Form Designer generated code
		/// <summary>
		/// Metoda wymagana do obsługi Designera - nie należy modyfikować
		/// zawartości tej metody przy użyciu edytora kodu.
		/// </summary>
		private void InitializeComponent()
		{
			this.components = new System.ComponentModel.Container();
			this.lblStartTime = new System.Windows.Forms.Label();
			this.btnStop = new System.Windows.Forms.Button();
			this.label1 = new System.Windows.Forms.Label();
			this.clhBytesSent = new System.Windows.Forms.ColumnHeader();
			this.label3 = new System.Windows.Forms.Label();
			this.lstDisplay = new System.Windows.Forms.ListView();
			this.clhMachineName = new System.Windows.Forms.ColumnHeader();
			this.tmrUpdateDisplay = new System.Windows.Forms.Timer(this.components);
			this.btnStartSniff = new System.Windows.Forms.Button();
			this.lblTotal = new System.Windows.Forms.Label();
			this.SuspendLayout();
			// 
			// lblStartTime
			// 
			this.lblStartTime.Location = new System.Drawing.Point(200, 24);
			this.lblStartTime.Name = "lblStartTime";
			this.lblStartTime.Size = new System.Drawing.Size(88, 16);
			this.lblStartTime.TabIndex = 2;
			this.lblStartTime.Text = "n/a";
			// 
			// btnStop
			// 
			this.btnStop.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right);
			this.btnStop.Enabled = false;
			this.btnStop.Location = new System.Drawing.Point(384, 16);
			this.btnStop.Name = "btnStop";
			this.btnStop.Size = new System.Drawing.Size(80, 23);
			this.btnStop.TabIndex = 0;
			this.btnStop.Text = "Stop";
			this.btnStop.Click += new System.EventHandler(this.btnStop_Click);
			// 
			// label1
			// 
			this.label1.Location = new System.Drawing.Point(8, 24);
			this.label1.Name = "label1";
			this.label1.Size = new System.Drawing.Size(88, 16);
			this.label1.TabIndex = 2;
			this.label1.Text = "Suma komputerów:";
			// 
			// clhBytesSent
			// 
			this.clhBytesSent.Text = "Liczba wysłanych bajtów";
			this.clhBytesSent.Width = 113;
			// 
			// label3
			// 
			this.label3.Location = new System.Drawing.Point(120, 24);
			this.label3.Name = "label3";
			this.label3.Size = new System.Drawing.Size(88, 16);
			this.label3.TabIndex = 2;
			this.label3.Text = "Początek nasłuchu:";
			// 
			// lstDisplay
			// 
			this.lstDisplay.Anchor = System.Windows.Forms.AnchorStyles.None;
			this.lstDisplay.Columns.AddRange(new System.Windows.Forms.ColumnHeader[] {
																						 this.clhMachineName,
																						 this.clhBytesSent});
			this.lstDisplay.FullRowSelect = true;
			this.lstDisplay.GridLines = true;
			this.lstDisplay.Location = new System.Drawing.Point(8, 48);
			this.lstDisplay.MultiSelect = false;
			this.lstDisplay.Name = "lstDisplay";
			this.lstDisplay.Size = new System.Drawing.Size(456, 280);
			this.lstDisplay.Sorting = System.Windows.Forms.SortOrder.Ascending;
			this.lstDisplay.TabIndex = 1;
			this.lstDisplay.View = System.Windows.Forms.View.Details;
			this.lstDisplay.DoubleClick += new System.EventHandler(this.lstDisplay_OnDoubleClick);
			// 
			// clhMachineName
			// 
			this.clhMachineName.Text = "Nazwa komputera";
			this.clhMachineName.Width = 306;
			// 
			// tmrUpdateDisplay
			// 
			this.tmrUpdateDisplay.Interval = 2000;
			this.tmrUpdateDisplay.Tick += new System.EventHandler(this.tmrUpdateDisplay_Tick);
			// 
			// btnStartSniff
			// 
			this.btnStartSniff.Anchor = (System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right);
			this.btnStartSniff.Location = new System.Drawing.Point(296, 16);
			this.btnStartSniff.Name = "btnStartSniff";
			this.btnStartSniff.Size = new System.Drawing.Size(80, 23);
			this.btnStartSniff.TabIndex = 0;
			this.btnStartSniff.Text = "Start";
			this.btnStartSniff.Click += new System.EventHandler(this.btnStartSniff_Click);
			// 
			// lblTotal
			// 
			this.lblTotal.Location = new System.Drawing.Point(88, 24);
			this.lblTotal.Name = "lblTotal";
			this.lblTotal.Size = new System.Drawing.Size(32, 16);
			this.lblTotal.TabIndex = 2;
			this.lblTotal.Text = "0";
			// 
			// frmMain
			// 
			this.AutoScaleBaseSize = new System.Drawing.Size(5, 13);
			this.ClientSize = new System.Drawing.Size(480, 341);
			this.Controls.AddRange(new System.Windows.Forms.Control[] {
																		  this.lblStartTime,
																		  this.label3,
																		  this.lblTotal,
																		  this.label1,
																		  this.btnStop,
																		  this.lstDisplay,
																		  this.btnStartSniff});
			this.Name = "frmMain";
			this.Text = "Program .NET Framework do podsłuchu sieci";
			this.ResumeLayout(false);

		}
		#endregion

		/// <summary>
		/// Główny punkt wejścia dla aplikacji.
		/// </summary>
		[STAThread]
		static void Main() 
		{
			Application.Run(new frmMain());
		}

		private void processData()
		{
			try
			{
				byte[] data = new byte[10240];				
				EndPoint remoteEP = this.listenerSocket.LocalEndPoint;
				int bytesrec = 0;			
				
				while(listen)
				{
					bytesrec = listenerSocket.ReceiveFrom(data, ref remoteEP);
					
					StringBuilder sb = new StringBuilder();
					foreach(byte b in data)
					{
						if(b<127 && b > 0)
						{
							sb.Append((char)b);
						}
						else
						{
							//sb.Append('.');
						}
					}

					string content = sb.ToString();					
					string t = remoteEP.ToString().Trim();
					string dnsName;
					t = t.Remove (t.Length-2, 2);
				
					if (dnsLookupTable.Contains (t)) 
					{						
						((MachineData)table[dnsLookupTable[t]]).BytesCaptured = ((MachineData)table[dnsLookupTable[t]]).BytesCaptured + bytesrec;
						((MachineData)table[dnsLookupTable[t]]).CapturedData.Append(content);
					} 
					else 
					{
						try
						{					
							dnsName = Dns.GetHostByAddress (t).HostName;
							dnsLookupTable.Add (t, dnsName);
							table.Add(dnsName, new MachineData(content, bytesrec));
						} 
						catch (Exception e) 
						{
							//MessageBox.Show(e.ToString());
						}				
					}		
				}
			}
			catch(Exception e)
			{
				MessageBox.Show(e.ToString());
			}
		}

		private void btnStartSniff_Click(object sender, System.EventArgs e)
		{
			listen = true;
			Thread sniffingThread = new Thread(new ThreadStart(processData));		
			sniffingThread.Start();
			this.btnStartSniff.Enabled = false;
			this.btnStop.Enabled = true;
			this.tmrUpdateDisplay.Enabled = true;
			this.lblStartTime.Text = DateTime.Now.ToLongTimeString();
		}

		private void tmrUpdateDisplay_Tick(object sender, System.EventArgs e)
		{			
			SortedList copyTable = new SortedList(table);
			SortedList newEntries = new SortedList();
			string[] items = new string[2];
			ListViewItem entry = null;
			int bytesrec = 0;
			
			this.lstDisplay.BeginUpdate();
			if(lastTable == null) //Pierwsza iteracja
			{
				foreach (DictionaryEntry en in copyTable) 
				{				
					items[0] = (string)en.Key;
					bytesrec = ((MachineData)en.Value).BytesCaptured;
					items[1] = bytesrec.ToString();
					entry = new ListViewItem(items);	
					this.lstDisplay.Items.Add(entry);
				}
			}
			else
			{
				int keyIndex = 0;
				foreach (DictionaryEntry en in copyTable) 
				{						
					keyIndex = lastTable.IndexOfKey(en.Key);
					if(keyIndex > -1)
					{
						bytesrec = ((MachineData)en.Value).BytesCaptured;
						this.lstDisplay.Items[keyIndex].SubItems[1].Text = bytesrec.ToString();
					}
					else
					{
						newEntries.Add(en.Key, en.Value);						
					}					
				}

				foreach (DictionaryEntry en in newEntries)
				{
					items[0] = (string)en.Key;
					bytesrec = ((MachineData)en.Value).BytesCaptured;
					items[1] = bytesrec.ToString();
					entry = new ListViewItem(items);	
					this.lstDisplay.Items.Add(entry);
				}
			}						
			
			this.lstDisplay.EndUpdate();
			this.lblTotal.Text = this.lstDisplay.Items.Count.ToString();
			lastTable = new SortedList(copyTable);
		}

		private void btnStop_Click(object sender, System.EventArgs e)
		{
			listen = false;
			this.btnStartSniff.Enabled = true;
			this.btnStop.Enabled = false;
			this.tmrUpdateDisplay.Enabled = false;
		}	

		private void lstDisplay_OnDoubleClick(object sender, System.EventArgs e)
		{
			if(listen)
			{
				MessageBox.Show("W celu wyświetlenia danych należy zatrzymać nasłuch");
				return;
			}
			else
			{
				if (this.lstDisplay.SelectedItems.Count == 1) 
				{
					string key = this.lstDisplay.SelectedItems[0].Text.ToString();
					if(table.Contains(key))
					{
						MachineData entry = (MachineData)table[key];
						frmViewData viewdata = new frmViewData();
						viewdata.Show();
						viewdata.SetText(entry.CapturedData.ToString());
						//MessageBox.Show(entry.CapturedData.ToString());
					}
					else
					{
						MessageBox.Show("Nie odnaleziono danych dla tego wpisu");
					}				
				}
			}
		}
	}

	public class MachineData
	{
		public MachineData()
		{
			CapturedData = new StringBuilder();
			BytesCaptured = 0;
		}

		public MachineData(string data, int bytes)
		{			
			CapturedData = new StringBuilder(data);
			BytesCaptured = bytes;
		}

		public StringBuilder CapturedData;		
		public int BytesCaptured;
	}
}
