dot1x.cpp

来自「802.1x认证客户端」· C++ 代码 · 共 407 行

CPP
407
字号
//ThorClient 802.1x (using WinPcap 3.2 alpha1)
//Download: http://thorqq.ys168.com 
//E-mail:   thorqq@163.com 
//Author:   Thorqq
//
//CDot1X类通过发送消息与外界通讯
//WM_DOT1X	          运行状态提示
//0 正常状态; 1 上线;-1 离线 -2 超时 
//公共接口函数:
//CDot1X(HWND hDlg);  类构造函数
//  输入参数:hDlg     -- 主窗口句柄
//~CDot1X();          类析构函数
//void Connect(const USERDATA *UserData);     
//			 *UserData -- 用户数据(用户名,密码,网卡等信息)
//  打开网卡,启动线程,发送请求认证数据帧,触发认证
//void Diconnect();   终止线程,关闭网卡,发送离线请求数据帧,断开连接
//void ClosAdapter(); 关闭网卡
////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "Dot1X.h"
#include "md5.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////
CDot1X::CDot1X(const HWND hDlg) : m_hDlg(hDlg)
{
	m_hThread = NULL;
	m_fp = NULL;			// 网卡设备

	u_char mcast[6] = {0x01, 0x80, 0xc2, 0x00, 0x00, 0x03};//多播地址
	memcpy(m_DestMac, mcast, 6);//默认目的地址为多播地址
}

CDot1X::~CDot1X()
{
	
}

bool CDot1X::OpenAdapter()
{	
	bpf_u_int32 netmask = 0;              
	char pcap_filter[100];              //filter space
	struct bpf_program pcap_fp;         //hold the compiled filter.
	char errbuf[PCAP_ERRBUF_SIZE] = "";
	//打开网卡
	if(!(m_fp = pcap_open_live(m_pData->nic,// name of the device
								256,		// portion of the packet to capture, max 65536
								0,			// promiscuous mode closed
								10,			// read timeout
								errbuf)))	// error buffer
	{
//		AfxMessageBox(errbuf);
		::SendMessage(m_hDlg, WM_DOT1X, -1, (long)errbuf);
		return false;
	}
	//设置过滤器,只接收802.1X的帧
	sprintf(pcap_filter, "ether dst %x:%x:%x:%x:%x:%x and ether proto 0x888e",
				      m_pData->mac[0],m_pData->mac[1],m_pData->mac[2],
					  m_pData->mac[3],m_pData->mac[4],m_pData->mac[5]);
//	sprintf(pcap_filter, " or ether dst 01:80:c2:00:00:03 and ether proto 0x888e",
//					  m_pData->Mac[0],m_pData->Mac[1],m_pData->Mac[2],
//					  m_pData->Mac[3],m_pData->Mac[4],m_pData->Mac[5]);

	if (pcap_compile(m_fp, &pcap_fp, pcap_filter, 0, netmask) == -1)
		return false;

	if (pcap_setfilter(m_fp, &pcap_fp) == -1)
		return false;
	
	return true;
}

DWORD WINAPI WorkProc(LPVOID lpParam)
{	//帧处理线程
	CDot1X *pTalk = (CDot1X *) lpParam;

	//提升线程优先级
	::SetThreadPriority(::GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);

	struct pcap_pkthdr *header;
	const u_char *pkt_data;

	int res;
	DWORD dwTick, dwOldTick;
	dwTick = dwOldTick = ::GetTickCount();
	int flag = 0;
	//逐个读取接收到的帧
	while((res = pcap_next_ex( pTalk->m_fp, &header, &pkt_data)) >= 0)
	{
		if(res == 0)// Timeout elapsed
		{	
			//用::GetTickCount()取得并计算超时的时间
			dwTick = ::GetTickCount();
			if(dwTick - dwOldTick >= 90000 //ms
				&& pTalk->m_pData->relogin == 1)
			{
				::SendMessage(pTalk->m_hDlg, WM_DOT1X, -2, (long)"超时,重新连接");
				dwOldTick = ::GetTickCount();
			}
			continue;
		}

		PPKTHDR pbuf = (PPKTHDR)pkt_data;
		memcpy(pTalk->m_DestMac, pbuf->SourMAC, 6);//取得接收到帧的源地址
		
		if(pbuf->Code == EAP_REQUEST)
		{
			switch(pbuf->EapType)
			{
			case EAP_KEEPONLINE:
				pTalk->SendKeeponline(pbuf->Id);
				dwOldTick = ::GetTickCount();
				break;
			case EAP_NOTIFICATION:
				pTalk->SendVersion(pbuf->Id);
				break;
			case EAP_IDENTIFY:	
				pTalk->SendUsername(pbuf->Id);
				break;
			case EAP_MD5:
				pTalk->SendPassword(pbuf->Id, ((PPASSWORDFRM) pkt_data)->Md5Pwd);
				break;
			default:	;
			}

			continue;
		}

		if(pbuf->Code == EAP_SUCCESS)
		{
			::SendMessage(pTalk->m_hDlg, WM_DOT1X, 1, (long)"认证成功 :)");
			continue;
		}

		if(pbuf->Code == EAP_FAILURE)
		{
			if(pbuf->EapType == EAP_LOGOUT)
				::SendMessage(pTalk->m_hDlg, WM_DOT1X, -1, (long)"断开连接");
			else
				::SendMessage(pTalk->m_hDlg, WM_DOT1X, -1, (long)pkt_data+0x18);
			break;//jump out of the loop
		}

		if(pbuf->Code == EAP_OTHER)//这里应该处理两个帧
		{//第一个是有关客户端下载地址的,这里不做处理
			if(flag == 0)
			{
				flag = 1;
				continue;
			}

			//认证之后的附加信息
			u_char a[0xff] = {0};
			memcpy(a, pkt_data+0x1a, *(pkt_data +0x11)-4);
//			a[*(pkt_data +0x11)-3] = '\0';
			for(int i= 0; i<0xff; i++)
			{
				if(a[0xff-i] == 0x34)
				{
					a[0xff-i] = '\t';
					a[0xff-i+1] = '\n';
					break;
				}
			}
			::SendMessage(pTalk->m_hDlg, WM_DOT1X, 0, (long)a);

			continue;
		}
	}
     
	return res;
}

void CDot1X::ClosAdapter()
{
//此函数会导致主线程阻塞,导致了消息循环的阻塞,最终导致工作线程Crash掉
//	::WaitForSingleObject(m_hThread, 400);//INFINITE);
/////////////////////////////////
/*	while(TRUE)
	{

		DWORD result ; 
		MSG msg ; 

		result = MsgWaitForMultipleObjects(1, &m_hThread, 
			FALSE, INFINITE, QS_ALLINPUT); 

		if (result == (WAIT_OBJECT_0))
		{
			break;
		} 
		else 
		{ 
			PeekMessage(&msg, NULL, 0, 0, PM_REMOVE);
			DispatchMessage(&msg); 
		} 
	}
*/////////////////////////////////////
	::CloseHandle(m_hThread);//关闭线程

	if(m_fp != NULL) 
		pcap_close(m_fp); //关闭网卡
}

void CDot1X::Connect(const USERDATA *pUserData)
{
	m_pData = pUserData;
	::SendMessage(m_hDlg, WM_DOT1X, 0, (long)"正在初始化网络适配器...");
	
	if(!OpenAdapter())//打开网卡,设置过滤,启动线程
	{
//		::SendMessage(m_hDlg, WM_DOT1X, -1, (long)"初始化网络适配器...失败");
		return;
	}

	//启动帧处理线程
	m_hThread = ::CreateThread(NULL, 0, WorkProc, this, 0, NULL);

	SendLogin();
}

void CDot1X::Disconnect()
{
	if(m_fp)
	{
		SendLogout();
		::SendMessage(m_hDlg, WM_DOT1X, -1, (long)"断开连接");

	//	m_bQuit = true;
//		ClosAdapter();
		::SendMessage(m_hDlg, WM_DOT1X, 0, (long)"网络适配器已关闭");
	}
}

bool CDot1X::SendLogin()
{	//发送EAPOL-START帧
	u_char buf[100] = {	0 };
	PLOGINFRM pbuf = (PLOGINFRM) buf;

	InitBuf(buf);//Set Dest MAC, Source MAC, Protocol Type and Version
	if(m_pData->morb == 'b')
		memset(buf, 0xff, 6);//广播发送EAPOL-START(默认为多播发送)
	pbuf->PktType = 0x01;		 //EAPOL-START

	if(!pcap_sendpacket(m_fp, buf, 60))
	{
		::SendMessage(m_hDlg, WM_DOT1X, 0, (long)"查找接入设备...");
		return true;
	}

	return false;
}

bool CDot1X::SendLogout()
{	//发送断线请求帧
	u_char buf[100] = {	0 };
	PLOGOUTFRM pbuf = (PLOGOUTFRM) buf;

	InitBuf(buf);//set Dest MAC, Source MAC, Protocol Type and Version
	pbuf->PktType = 0x02;//EAPOL-LOGOUT

	if(!pcap_sendpacket(m_fp, buf, 60))
	{
		::SendMessage(m_hDlg, WM_DOT1X, 0, (long)"正在断开连接...");
		return true;
	}

	return false;
}

bool CDot1X::SendVersion(const u_char Id)
{	//发送版本确认帧
/*	u_char buf[100] = {

          0x2e, 0x25, 0x4d, 0x3b, 0x5f, 0x43, 0x5f,
	0x5d, 0x40, 0x5d, 0x5f, 0x5e, 0x5c, 0x6d, 0x6d,
	0x6d, 0x6d, 0x6d, 0x6d, 0x6d,
*/
	u_char buf[100] = {
	0x01, 0x80, 0xc2, 0x00, 0x00, 0x03, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x88, 0x8e, 0x01, 0x00, 
    0x00, 0x31, 0x02, 0x01 ,0x00, 0x31, 0x02, 0x01,
	0x16, 
////
	      0x2e, 0x25, 
////
		              0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
	0x00, 0x00, 0x00, 0x00, 0x00,
	
	                              0x02, 0x16, 0x5f, 
	0x59, 0x55, 0x5e, 0xbb, 0x5c, 0x54, 0x58, 0x5a,
	0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d, 0x6d,
	0x6d, 0x6d, 0x6d
	}; 
//	PVERSIONFRM pbuf = (PVERSIONFRM) m_buf;
	InitBuf(buf);
	if(!pcap_sendpacket(m_fp, buf, 67))
	{
		::SendMessage(m_hDlg, WM_DOT1X, 0, (long)"查找认证服务器...");
		return true;
	}
	
	return false;
}

bool CDot1X::SendUsername(const u_char Id)
{	//发送用户名帧
	u_char buf[100] = { 0 };
	PUSERNAMEFRM pbuf = (PUSERNAMEFRM) buf;

	InitBuf(buf);
	pbuf->Hdr.Len1 = ::htons(strlen(m_pData->username) + 0x0b);
	pbuf->Hdr.Code = 0x02;
	pbuf->Hdr.Id = Id;
	pbuf->Hdr.Len2 = pbuf->Hdr.Len1;
	pbuf->Hdr.EapType = 0x01;
	pbuf->Unknown[0] = 0x15;
	pbuf->Unknown[1] = 0x04;
	if(m_pData->updateip == 0x01)
		memcpy(pbuf->Ip, m_pData->ip, 4);//上传本机IP
	memcpy(&pbuf->Username, &m_pData->username, strlen(m_pData->username));
	
	if(!pcap_sendpacket(m_fp, buf, 60))
	{
		::SendMessage(m_hDlg, WM_DOT1X, 0, (long)"验证用户名...");
		return true;
	}
	
	return false;
}

bool CDot1X::SendPassword(const u_char Id, const u_char *Chap)
{	//	发送密码帧
	u_char buf[100] = { 0 };
	PPASSWORDFRM pbuf = (PPASSWORDFRM) buf;

	InitBuf(buf);
	pbuf->Hdr.Len1 = ::htons(strlen(m_pData->username) + 0x16);
	pbuf->Hdr.Code = 0x02;
	pbuf->Hdr.Id = Id;
	pbuf->Hdr.Len2 = pbuf->Hdr.Len1;
	pbuf->Hdr.EapType = 0x04;
	pbuf->Unknown[0] = 0x10;
	SetMd5Buf(pbuf, Id, Chap);//计算MD5
	memcpy(pbuf->Username, m_pData->username, strlen(m_pData->username));

	if(!pcap_sendpacket(m_fp, buf, 60))
	{
		::SendMessage(m_hDlg, WM_DOT1X, 0, (long)"验证密码...");
		return true;
	}
	
	return false;
}

bool CDot1X::SendKeeponline(const u_char Id)
{	//发送保持在线帧
	u_char buf[100] = { 0 };
	PKEEPONLINEFRM pbuf = (PKEEPONLINEFRM) buf;

	InitBuf(buf);
	pbuf->Hdr.Len1 = ::htons(strlen(m_pData->username) + 0x0b);
	pbuf->Hdr.Code = 0x02;
	pbuf->Hdr.Id = Id;
	pbuf->Hdr.Len2 = pbuf->Hdr.Len1;
	pbuf->Hdr.EapType = 0x14;
	pbuf->Unknown[0] = 0x00;
	pbuf->Unknown[1] = 0x15;
	pbuf->Unknown[2] = 0x04;
	if(m_pData->updateip == 0x01)
		memcpy(pbuf->Ip, m_pData->ip, 4);
	memcpy(pbuf->Username, m_pData->username, strlen(m_pData->username));
	
	return !pcap_sendpacket(m_fp, buf, 60);
}
void CDot1X::SetMd5Buf(PPASSWORDFRM pBuf, const u_char ID, const u_char *chap)
{	//计算MD5
	u_char TmpBuf[1 + 64 + 16]; 
	MD5_CTX md5T; 
	u_char digest[16]; 
	int PasswdLen = strlen(m_pData->password);
	TmpBuf[0] = ID;//	memcpy(TmpBuf + 0x00, ID, 1); 
	memcpy(TmpBuf + 0x01, m_pData->password, PasswdLen); 
	memcpy(TmpBuf + 0x01 + PasswdLen, chap, 16); 
	md5T.MD5Update(TmpBuf, 17 + PasswdLen); 
	md5T.MD5Final(digest); 
	memcpy(pBuf->Md5Pwd, digest, 16); 
}

void CDot1X::InitBuf(u_char *buf)
{	//初始化各个帧
	u_char prototype[3] = {0x88, 0x8e, 0x01};

	if(m_pData->multisend == 0x00)	 //set destination MAC
		memcpy(buf, m_DestMac, 6);
	memcpy(buf+6 , m_pData->mac, 6); //set source MAC
	memcpy(buf+12, prototype,    3); //set protocol type and its version
}

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?