Click here to Skip to main content
15,886,689 members
Articles / Desktop Programming / MFC

Network Sniffer

Rate me:
Please Sign up or sign in to vote.
4.71/5 (28 votes)
19 Jul 2004CPOL 262.7K   14.7K   116  
Network Sniffer
// SnifferDlg.cpp : implementation file
//

#include "stdafx.h"
#include "Sniffer.h"
#include "SnifferDlg.h"

#ifdef _DEBUG
#define new DEBUG_NEW
#endif


inline CString GetNiceString(LPCTSTR pString)
{
	const int numDots = 40 - _tcslen(pString);

	CString dotString(_T('.'),numDots);


	CString s = CString(pString) + dotString + CString(_T(" "));

	return s;
}


// CAboutDlg dialog used for App About

class CAboutDlg : public CDialog
{
public:
	CAboutDlg();

// Dialog Data
	enum { IDD = IDD_ABOUTBOX };

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support

// Implementation
protected:
	DECLARE_MESSAGE_MAP()
};

CAboutDlg::CAboutDlg() : CDialog(CAboutDlg::IDD)
{
}

void CAboutDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
}

BEGIN_MESSAGE_MAP(CAboutDlg, CDialog)
END_MESSAGE_MAP()


// CSnifferDlg dialog



CSnifferDlg::CSnifferDlg(CWnd* pParent /*=NULL*/)
	: CDialog(CSnifferDlg::IDD, pParent)
	, m_SelInterface(_T(""))
{
	m_hIcon = AfxGetApp()->LoadIcon(IDR_MAINFRAME);

	Init();
}

void CSnifferDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialog::DoDataExchange(pDX);
	DDX_Control(pDX, IDC_CMB_INTERFACES, m_CmbInterfaces);
	DDX_Control(pDX, IDC_FRM_INTERFACES, m_FrmInterfaces);
	DDX_Control(pDX, IDC_STARTSNIFFING, m_BtnStartSniffing);
	DDX_Control(pDX, IDC_TV_PACKETS, m_TVPackets);
}

BEGIN_MESSAGE_MAP(CSnifferDlg, CDialog)
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	//}}AFX_MSG_MAP
	ON_WM_SIZE()
	ON_BN_CLICKED(IDC_STARTSNIFFING, OnBnClickedStartsniffing)
	ON_WM_CLOSE()
END_MESSAGE_MAP()


// CSnifferDlg message handlers

BOOL CSnifferDlg::OnInitDialog()
{
	CDialog::OnInitDialog();

	// Add "About..." menu item to system menu.

	// IDM_ABOUTBOX must be in the system command range.
	ASSERT((IDM_ABOUTBOX & 0xFFF0) == IDM_ABOUTBOX);
	ASSERT(IDM_ABOUTBOX < 0xF000);

	CMenu* pSysMenu = GetSystemMenu(FALSE);
	if (pSysMenu != NULL)
	{
		CString strAboutMenu;
		strAboutMenu.LoadString(IDS_ABOUTBOX);
		if (!strAboutMenu.IsEmpty())
		{
			pSysMenu->AppendMenu(MF_SEPARATOR);
			pSysMenu->AppendMenu(MF_STRING, IDM_ABOUTBOX, strAboutMenu);
		}
	}

	// Set the icon for this dialog.  The framework does this automatically
	//  when the application's main window is not a dialog
	SetIcon(m_hIcon, TRUE);			// Set big icon
	SetIcon(m_hIcon, FALSE);		// Set small icon

	// TODO: Add extra initialization here
	for ( int i = 0 ; i < m_Interfaces.GetSize(); i ++ )
	{
		m_CmbInterfaces.AddString( m_Interfaces.GetAt(i) );
	}

	m_CmbInterfaces.SetCurSel( 0 );

	m_IL.Create(16,16,ILC_COLOR32 | ILC_MASK, 0,1);

	m_IL.Add( LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_COMP2COMP ) ) );
	m_IL.Add( LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_IPHeader ) ) );
	m_IL.Add( LoadIcon(AfxGetInstanceHandle(), MAKEINTRESOURCE(IDI_TCPHEADER ) ) );

	m_TVPackets.SetImageList( &m_IL,TVSIL_NORMAL );

//	ShowWindow( SW_SHOWMAXIMIZED );
	ResizeForm();

	LOGFONT logFont;
	CFont* pFont = new CFont();

	RtlZeroMemory( &logFont, sizeof(logFont) );

	_tcscpy(logFont.lfFaceName, _T("Lucida Console"));
	logFont.lfHeight = 12;

	pFont->CreateFontIndirect( &logFont );

	m_TVPackets.SetFont( pFont, TRUE );
	



	return TRUE;  // return TRUE  unless you set the focus to a control
}

void CSnifferDlg::OnSysCommand(UINT nID, LPARAM lParam)
{
	if ((nID & 0xFFF0) == IDM_ABOUTBOX)
	{
		CAboutDlg dlgAbout;
		dlgAbout.DoModal();
	}
	else
	{
		CDialog::OnSysCommand(nID, lParam);
	}
}

// If you add a minimize button to your dialog, you will need the code below
//  to draw the icon.  For MFC applications using the document/view model,
//  this is automatically done for you by the framework.

void CSnifferDlg::OnPaint() 
{
	if (IsIconic())
	{
		CPaintDC dc(this); // device context for painting

		SendMessage(WM_ICONERASEBKGND, reinterpret_cast<WPARAM>(dc.GetSafeHdc()), 0);

		// Center icon in client rectangle
		int cxIcon = GetSystemMetrics(SM_CXICON);
		int cyIcon = GetSystemMetrics(SM_CYICON);
		CRect rect;
		GetClientRect(&rect);
		int x = (rect.Width() - cxIcon + 1) / 2;
		int y = (rect.Height() - cyIcon + 1) / 2;

		// Draw the icon
		dc.DrawIcon(x, y, m_hIcon);
	}
	else
	{
		CDialog::OnPaint();
	}
}

// The system calls this function to obtain the cursor to display while the user drags
//  the minimized window.
HCURSOR CSnifferDlg::OnQueryDragIcon()
{
	return static_cast<HCURSOR>(m_hIcon);
}

// Starts the sniffing process
bool CSnifferDlg::StartSniffing(void)
{
	SOCKET        s = INVALID_SOCKET;
	WSABUF        wbuf = {0};
	DWORD         dwBytesRet = 0, dwFlags = 0;
	unsigned int  optval = 0;
	char         *rcvbuf=NULL;
	int           rc = 0, err;


	
	if ( m_ulFilterMask & (FILTER_MASK_SOURCE_ADDRESS | FILTER_MASK_SOURCE_PORT) )
	{
		printf("Source address filter     : ");
		//PrintAddress((SOCKADDR *)&m_saSourceAddress, sizeof(m_saSourceAddress));
		printf("\n");
	}
	if ( m_ulFilterMask & (FILTER_MASK_DESTINATION_ADDRESS | FILTER_MASK_DESTINATION_PORT) )
	{
		printf("Destination address filter: ");
		//PrintAddress((SOCKADDR *)&g_saDestinationAddress, sizeof(g_saDestinationAddress));
		printf("\n");
	}

	//
	// Create a raw socket for receiving IP datagrams
	//
	s = WSASocket(AF_INET, SOCK_RAW, IPPROTO_IP, NULL, 0, WSA_FLAG_OVERLAPPED);
	if (s == INVALID_SOCKET)
	{
		printf("WSASocket() failed: %d\n", WSAGetLastError());
		return false;
	}

	//
	// This socket MUST be bound before calling the ioctl
	//

	sockaddr_in sa;
	sa.sin_family = AF_INET;
	sa.sin_addr.s_addr = inet_addr(m_SelInterface);
	sa.sin_port = INADDR_ANY;//htons(7000);

	rc = bind(s, (SOCKADDR *)&sa, sizeof(sa));
	if (rc == SOCKET_ERROR)
	{
		printf("bind() failed: %d\n", WSAGetLastError());
		if (INVALID_SOCKET != s)
		{
			closesocket(s);
			s = INVALID_SOCKET;
		}
		WSACleanup();
		return false;
	}
	printf("Binding to: ");
	//PrintAddress((SOCKADDR *)&g_saLocalInterface, sizeof(g_saLocalInterface));
	printf("\n");

	//
	// Set the SIO_RCVALLxxx ioctl
	//
	optval = 1;
	rc = WSAIoctl(s, SIO_RCVALL, &optval, sizeof(optval),
		NULL, 0, &dwBytesRet, NULL, NULL);
	if (rc == SOCKET_ERROR)
	{
		printf("WSAIotcl(0x%x) failed: %d\n", SIO_RCVALL,
			(err = WSAGetLastError()));
		if (err == WSAEINVAL)
		{
			printf("NOTE: IPv6 does not currently support the SIO_RCVALL* ioctls\n");
		}

		if (INVALID_SOCKET != s)
		{
			closesocket(s);
			s = INVALID_SOCKET;
		}
		WSACleanup();
		return false;
	}

	//
	// Allocate a buffer for receiving data
	//
	rcvbuf = (char *)HeapAlloc(GetProcessHeap(), 0, MAX_IP_SIZE);
	if (rcvbuf == NULL)
	{
		fprintf(stderr, "HeapAlloc failed: %d\n", GetLastError());
		if (INVALID_SOCKET != s)
		{
			closesocket(s);
			s = INVALID_SOCKET;
		}
		WSACleanup();
		return false;
	}

	//
	// Start receiving IP datagrams until interrupted
	// 
	while ( !m_bExit )
	{
		wbuf.len = MAX_IP_SIZE;
		wbuf.buf = rcvbuf;
		dwFlags  = 0;

		rc = WSARecv(s, &wbuf, 1, &dwBytesRet, &dwFlags, NULL, NULL);
		if (rc == SOCKET_ERROR)
		{
			printf("WSARecv() failed: %d\n", WSAGetLastError());
			break;
		}

		DecodePacket(rcvbuf , dwBytesRet );


	}
	//
	// Cleanup
	//

	if (rcvbuf) 
		HeapFree(GetProcessHeap(), 0, rcvbuf);

	if (INVALID_SOCKET != s)
	{
		closesocket(s);
		s = INVALID_SOCKET;
	}
	WSACleanup();

	return true;
}

bool CSnifferDlg::Init(void)
{
	try
	{
		m_bExit = false;
		m_hThread = NULL;
		m_dwThreadID = 0;

		m_ulFilterMask = 0;

		m_Logger.Open( _T("Logger.txt"),CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone);

		int           rc = 0;
		WSADATA       wsd;
		//
		// Load Winsock
		//
		if ((rc = WSAStartup(MAKEWORD(2,2), &wsd)) != 0)
		{
			printf("WSAStartup() failed: %d\n", rc);
			return false;
		}

		GetInterfaces( m_Interfaces );


		return true;
	}
	catch(...)
	{
		return false;
	}

}

// Decodes the packet
bool CSnifferDlg::DecodePacket(char* pData, DWORD dwSize)
{

	try
	{
	
		if (dwSize < sizeof(char))
			return false;
		
		// Check the IP version
		const int ip_version = HI_BYTE(*pData);
		
		CString source, destination;

		if ( ip_version == 4)
		{
			IPV4_HDR* pHeader;
			int headerLen = 0;

		
			// Verify the buffer is large enough
			if (dwSize  < sizeof(IPV4_HDR))
				return false;
			
			// Get length of IPv4 header to determine where next protocol header begins
			headerLen = LO_BYTE(*pData) * 4;
			
			pHeader = (IPV4_HDR *)pData;

			ConvertAddress( pHeader->ip_srcaddr, source );
			ConvertAddress( pHeader->ip_destaddr, destination);

			switch (pHeader->ip_protocol)
			{

			case IPPROTO_TCP:

				TCP_HDR* pTCPHeader;

				pTCPHeader = (TCP_HDR *) &pData[headerLen];
				
				ParseTCPPacket(source, destination, pTCPHeader, pData, dwSize - headerLen );

				break;

			default:

				//TRACE(_T("Not a TCP packet"));
				return false;

			}

		}
		else
		{
			//TRACE( _T("IP version 6") );
			return false;
		}

/*
	struct in_addr sa4 = {0};

	char* pAddress = NULL;

	//	sa4.S_un.sin_family = AF_INET;
	//    sa4.sin_port   = 0;

	sa4.S_un.S_addr = pIPHeader->ip_srcaddr;

	pAddress = inet_ntoa( sa4);
*/
		return true;
	}
	catch(...)
	{
		return false;
	}

	
}


bool CSnifferDlg::ParseTCPPacket(const CString& source, const CString& destination, TCP_HDR* pTCPHeader, char* pData, unsigned long len	)
{
	try
	{
		if ( len < sizeof(*pTCPHeader) )
			return false;

		len -= sizeof(*pTCPHeader);
		pData += sizeof(*pTCPHeader);

		unsigned int sourcePort, destPort;

		sourcePort = htons(pTCPHeader->src_portno);
		destPort = htons(pTCPHeader->dest_portno);
	
//		if ( sourcePort != 25 && destPort != 25 )
//			return false;

		Session* pSide = NULL;
		CString strError;

		INT_PTR arrayIndex = -1;

		for ( int i = 0 ; i < m_Array.GetSize(); i++ )
		{
			Session* pCur = m_Array.GetAt(i);

			if ( pCur->sourceIP == source && pCur->sourcePort == sourcePort 
				&& pCur->destIP == destination && pCur->destPort == destPort)
			{
				pSide = pCur;
				arrayIndex = i; // Save the position in the array
				break;
			}

		}

		const bool sideExist = (pSide != NULL );

		const tcp_seq curSeq = ntohl(pTCPHeader->seq_num);
		
		// if the packet is a SYN packet then don't count the gap
		const USHORT flg = (ntohs(pTCPHeader->lenflags) & 0x3F);


		if ( !pSide )  // A new session
		{
			// Only if the packet is a SYN packet then
			if ( (flg & TH_SYN ) )
			{
				pSide = new Session();

				pSide->sourceIP = source;
				pSide->destIP = destination;
				pSide->sourcePort = sourcePort;
				pSide->destPort = destPort;
				pSide->len = len;
				pSide->ISN = curSeq;
				pSide->pOtherSide = NULL;

				arrayIndex = m_Array.Add( pSide );

				strError = _T(" /*New session*/ ");
			}

		}


		// Make sure the gap in sequences is less than 10 MB and is positive
		if ( sideExist )
		{
			const long gap = curSeq - pSide->ISN;
			
			pSide->len = len;

			if ( (flg & TH_SYN) != TH_SYN )
			{
				
				if ( (flg & TH_RST ) || ( flg & TH_FIN) )
				{
					m_Array.RemoveAt( arrayIndex );
				}
				else
				{
					// Only if the size is greater than 0 do the checking
					if ( len > 0 )
					{

//						ASSERT( gap >= 0 );
						if ( gap < 0 )
							strError += _T(" { Gap < 0 }");

//						ASSERT ( gap <= 10 * 1024 * 1000 );

						if ( gap >  10 * 1024 * 1000 )
							strError += _T(" { Gap > 10 MB }");
					}
				}
			}
			else
				pSide->ISN = curSeq;
			
		}

//		if ( sourcePort == 25 || destPort == 25 )
			PrintPacket( source, destination, pTCPHeader, len, strError);


		return true;
		
	}
	catch(...)
	{
		return false;
	}
	
}


void CSnifferDlg::ConvertAddress( unsigned int address, CString& strAddress )
{
	IN_ADDR ad;

	ad.S_un.S_addr = address;

	strAddress = inet_ntoa( ad );
}


void CSnifferDlg::PrintSession( Session* pSession )
{


}


void CSnifferDlg::PrintPacket( const CString& source, const CString& destination,TCP_HDR* pTCPHeader, DWORD dwSize, const CString& errors ) 
{
	CString s, from, to, flags, sequence, ack ;

	from = source;
	to = destination;

	unsigned int sourcePort, destPort;

	sourcePort = htons(pTCPHeader->src_portno);
	destPort = htons(pTCPHeader->dest_portno);

	// if the packet is a SYN packet then don't count the gap
	const USHORT flg = (ntohs(pTCPHeader->lenflags) & 0x3F);

	if ( flg & TH_SYN ) 
	{
		if ( flags.GetLength() > 0 )
			flags += _T(",");
		flags += _T("SYN");

	}
	if ( flg & TH_RST ) 
	{
		if ( flags.GetLength() > 0 )
			flags += _T(",");
		flags += _T("RST");
	}

	if ( flg & TH_FIN ) 
	{
		if ( flags.GetLength() > 0 )
			flags += _T(",");
		flags += _T("FIN");
	}

	if ( flg & TH_ACK ) 
	{
		if ( flags.GetLength() > 0 )
			flags += _T(",");
		flags += _T("ACK");
	}

	if ( flg & TH_URG ) 
	{
		if ( flags.GetLength() > 0 )
			flags += _T(",");
		flags += _T("URG");
	}


	if ( flg & TH_PUSH ) 
	{
		if ( flags.GetLength() > 0 )
			flags += _T(",");
		flags += _T("PUSH");
	}

	if ( flg & TH_TAPI ) 
	{
		if ( flags.GetLength() > 0 )
			flags += _T(",");
		flags += _T("TAPI");
	}

	if ( flg & TH_NETDEV ) 
	{
		if ( flags.GetLength() > 0 )
			flags += _T(",");
		flags += _T("NETDEV");
	}

	sequence.Format(_T("%lu"),ntohl(pTCPHeader->seq_num));
	ack.Format(_T("%lu"),ntohl(pTCPHeader->ack_num) );

	s.Format(_T("%s - %s {%ld-%ld}, len: %ld, seq: %s,ack: %s, Flags: %s %s\r\n"), from, to, sourcePort, destPort, dwSize, sequence,ack, flags, errors);

	m_Logger.Write( (LPCTSTR)s, s.GetLength() * sizeof(TCHAR) );
	
	
	CString szSourcePort, szDestPort, szSize;
	
	szSourcePort.Format( _T("%ld"), sourcePort );
	szDestPort.Format( _T("%ld"), destPort );
	
	szSize.Format( _T("%ld"), dwSize );

	TVPacketItem packetItem(source, szSourcePort, destination, szDestPort, szSize, flags, sequence, ack);
	
	InsertPacketToTV( packetItem );

}
bool CSnifferDlg::GetInterfaces(CStringArray& interfaces)
{
	try
	{
	

		char     Hostname[MAX_PATH];
		HOSTENT *pHostEnt;
		int      nAdapter = 0;
		struct sockaddr_in   address;

		gethostname( Hostname, sizeof( Hostname ));
		pHostEnt = gethostbyname( Hostname );

		while ( pHostEnt->h_addr_list[nAdapter] )
		{
			memcpy(&address.sin_addr,pHostEnt->h_addr_list[nAdapter], pHostEnt->h_length);

			interfaces.Add(inet_ntoa(address.sin_addr));
			nAdapter++;
		} 

		return true;
	}
	catch(...)
	{
		return false;
	}
}
void CSnifferDlg::OnSize(UINT nType, int cx, int cy)
{
	CDialog::OnSize(nType, cx, cy);

	ResizeForm();
}

void CSnifferDlg::ResizeForm(void)
{
	if ( !IsWindowVisible() || !m_CmbInterfaces.IsWindowVisible() ||  !m_FrmInterfaces.IsWindowVisible() || !m_TVPackets.IsWindowVisible())
		return;
	
	CRect rect, dlgRect;

	GetClientRect( &dlgRect );

	rect = dlgRect;

	rect.top = 100;
	
	m_TVPackets.MoveWindow( &rect );
}

void CSnifferDlg::OnBnClickedStartsniffing()
{
	m_CmbInterfaces.GetLBText( m_CmbInterfaces.GetCurSel(), m_SelInterface );
	
	m_hThread = CreateThread(NULL, 0,SnifferThread,this,0,&m_dwThreadID);

	
}


ULONG WINAPI CSnifferDlg::SnifferThread(LPVOID pParam)
{
	
	CSnifferDlg* pDlg = (CSnifferDlg*)pParam;

	pDlg->StartSniffing();

	return S_OK;
}

void CSnifferDlg::OnClose()
{
	m_bExit = true;
		
	DWORD dwValue = WaitForSingleObject( m_hThread, 5000);

	if ( dwValue == WAIT_TIMEOUT )
		TerminateThread( m_hThread,0);

	CDialog::OnClose();
}

void CSnifferDlg::InsertPacketToTV(const TVPacketItem& packetItem)
{
	const CString caption = packetItem.m_SourceIP + CString(_T(" - ")) + packetItem.m_DestIP;

	HTREEITEM hItem = m_TVPackets.InsertItem( caption,0,0 );
	
	m_TVPackets.SetItemData( hItem, DWORD_PTR(&packetItem) );

	HTREEITEM hIPHeader = m_TVPackets.InsertItem(_T("IP Header"),1,1,hItem);

	CString str;

	str = GetNiceString(_T("Source IP:")) + packetItem.m_SourceIP;

	m_TVPackets.InsertItem(str,2,2,hIPHeader);

	str = GetNiceString(_T("Destination IP:")) + packetItem.m_DestIP;

	m_TVPackets.InsertItem(str,2,2,hIPHeader);

	HTREEITEM hTCPHeader = m_TVPackets.InsertItem(_T("TCP Header"),1,1,hItem);

	str = GetNiceString(_T("Source Port:")) + packetItem.m_SourcePort;

	m_TVPackets.InsertItem(str,2,2,hTCPHeader);

	str = GetNiceString(_T("Destination Port:")) + packetItem.m_DestPort;

	m_TVPackets.InsertItem(str,2,2,hTCPHeader);

	str = GetNiceString(_T("Size:")) + packetItem.m_Size;

	m_TVPackets.InsertItem(str,2,2,hTCPHeader);

	str = GetNiceString(_T("Flags:")) + packetItem.m_Flags;

	m_TVPackets.InsertItem(str,2,2,hTCPHeader);

	str = GetNiceString(_T("Sequence:")) + packetItem.m_Sequence;

	m_TVPackets.InsertItem(str,2,2,hTCPHeader);

	str = GetNiceString(_T("Ack:")) + packetItem.m_Ack;

	m_TVPackets.InsertItem(str,2,2,hTCPHeader);

}


By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer
Australia Australia
Been a programmer since 1999.
Experience in:
.Net, C++, C#, VB, VB.NET, ASP, ASP.NET, DLLs, COM etc.

Comments and Discussions