onetouchdlg.cpp

来自「可以一键自动检测局域网内的网关和未被占用的IP地址」· C++ 代码 · 共 2,298 行 · 第 1/5 页

CPP
2,298
字号
				index = i;	//为第一个,退出
				break;
			}
		}
	}*/

//	out.Format(out+"%d",index);
//	AfxMessageBox(out);

    if(index < 1 || index > total)
    {
        pcap_freealldevs(alldevs);
		p->m_info = "未检测到可用网卡,配置失败.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
        return 0;
    }	
    /* Jump to the selected adapter */
	i = 0;
    for(d=alldevs; i< index-1 ;d=d->next, i++);    

	//////////////
    /* Open the adapter */
    if ( (adhandle= pcap_open_live(d->name,  // name of the device
                             65536,     // portion of the packet to capture.  制定pcap捕获的最大数目的网络数据包;
                                        // 65536 grants that the whole packet will be captured on all the MACs.
                             1,        // promiscuous mode 混杂模式
                             -1,      // read timeout  制定经过特定时间(ms)后读超时;0表示遇到错误退出,-1指定永不超时;
                                   // remote authentication
                             errbuf     // error buffer
                             ) ) == NULL)
    {
        pcap_freealldevs(alldevs);
		p->m_info = "Unable to open the adapter.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
        return 0;
    }    
    /* Check the link layer. We support only Ethernet for simplicity. *///检测链路层
    if(pcap_datalink(adhandle) != DLT_EN10MB)
    {//仅支持以太网,其他类型帧头无法解析        
		pcap_freealldevs(alldevs);
		p->m_info = "This program works only on Ethernet networks.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
        return 0;
    }    
    if(d->addresses != NULL)
	{
        /* Retrieve the mask of the first address of the interface */
        netmask=((struct sockaddr_in *)(d->addresses->netmask))->sin_addr.S_un.S_addr;
		netip = ((struct sockaddr_in *)(d->addresses->addr))->sin_addr.S_un.S_addr; //ip addr
	}
    else
        /* If the interface is without addresses we suppose to be in a C class network */
        netmask=0xffffff;  

	////////////////获取网卡mac
	LPADAPTER lpAdapter =  PacketOpenAdapter(d->name);
	PACKET_OID_DATA*  OidData;   
	OidData = (PACKET_OID_DATA *)malloc(6 + sizeof(PACKET_OID_DATA));
	OidData->Oid = OID_802_3_CURRENT_ADDRESS;//Oid=OID_802_3_PERMANENT_ADDRESS;
	OidData->Length = 6;
	ZeroMemory(OidData->Data, 6);
	BOOLEAN Status = PacketRequest(lpAdapter, FALSE, OidData);
	unsigned char  localMAC[6];
	if(Status)
	{
		for(int k=0; k<6; k++)
			localMAC[k] = OidData->Data[k];
	}
	else
	{
		pcap_freealldevs(alldevs);
		p->m_info = "Error retrieving the MAC address of the adapter!";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
        return 0;
	}
	free(OidData);
	PacketCloseAdapter(lpAdapter);
	////

	pcap_freealldevs(alldevs);  

////////////////////////////////
/////重复检测detectTimes次数后放弃
int detectTimes = 3;
while(detectTimes-- != 0)
{
	repIP_List.clear();

	/////////////compile the filter
	char* packet_filter = "arp or udp or igmp";//过滤规则
    if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
    {     
		p->m_info = "Unable to compile the packet filter. Check the syntax.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
        return 0;
    }    
    ////////////////set the filter
    if (pcap_setfilter(adhandle, &fcode)<0)
    {
		p->m_info = "Error setting the filter.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
        return 0;
    }   

	/////////////////////////////////
    ///截获一个符合假设要求的数据包
	p->m_info = "正在获取网关信息,请稍候...";
	m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
	pcap_loop(adhandle, 1, packet_handler, NULL);//超时时间设为-1,即这里读取n个数据包后立即退出。
	//超时设为T,则这里必须达到T时刻并且读取完n个数据包才退出。超时设为0则勇不退出.

	//经过callback函数处理后,得到srcIP 和 dstIP
	if(srcIP.byte1 == 0 && srcIP.byte2 == 0 && srcIP.byte3 == 0 && srcIP.byte4 == 0
		|| dstIP.byte1 == 0 && dstIP.byte2 == 0 && dstIP.byte3 == 0 && dstIP.byte4 == 0)
	{
		pcap_freealldevs(alldevs);
		p->m_info = "ERROR: 未截获到数据包!";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		continue;
	}

	////////////////////////////////
	///采用pcap_send_queue队列发送arp请求.假设第一步截取到的数据包的srcIP或dstIP中含有实际的网段地址。
	///分别以这两个网段构造2*254个arp请求包。如果有响应,则为正确的网段。此过程可重复两三次,提高假设命中的率
	pcap_send_queue *squeue;//发送队列
	int caplen   =   2*254*76;   //2*254个arp请求 76=16+60; 16是pcap_pkthdr大小,60是arp含ether头大小
	int sync = 0, res=0;   
	pcap_pkthdr header;
	pcap_pkthdr *pktheader = &header;
	bool sent = false;

	pktheader->len = 60;//设置pcap头大小(独立与数据包)
	pktheader->caplen = 60;
	squeue = pcap_sendqueue_alloc(caplen);  //分配队列缓存
	
	///////////////////////////////
	////生成发送队列
	//构造完整arp包
	u_char packet[42];
	ethernet_header eh;
	arp_frame af;
//	char ip1[15],ip2[15];

/*	localMAC[0] = 0x00;
	localMAC[1] = 0x1e;
	localMAC[2] = 0x4f;
	localMAC[3] = 0xea;
	localMAC[4] = 0x9e;
	localMAC[5] = 0xa7;*/

	for(int k = 0 ;k < 6 ;k ++)
	{
		eh.eth_dest[k] = 0xff;//广播
		eh.eth_src[k] = localMAC[k];//随便
		af.Send_HW_Addr[k] = localMAC[k];//自己mac 关系mac-ip绑定
		af.Targ_HW_Addr[k] = 0xff;//随便
	}
	eh.eth_type = htons(0x0806);//0806为arp
    af.HW_Type = htons(0x0001);
    af.Prot_Type = htons(0x0800);
    af.HW_Addr_Len = 6;
    af.Prot_Addr_Len = 4;
    af.Opcode = htons(0x0001);//1 request 2 apply
	
//	sprintf(ip, "%d.%d.%d.%d", netip&0xff,netip>>8&0xff,netip>>16&0xff,netip>>24&0xff);//从网卡中获取的自己ip
    af.Send_Prot_Addr = inet_addr("127.0.0.1");//自己ip设为127.0.0.1

    memset(packet, 0 , sizeof(packet));
    memcpy(packet, &eh, sizeof(eh));
    memcpy(packet + sizeof(eh), &af, sizeof(af));

/*	packet[38] = 172;
		packet[39] = 16;
		packet[40] = 65;
		packet[41] = 3;
		if (pcap_sendqueue_queue(squeue, pktheader, packet) == -1)
			{
				sent = false;
				printf("Warning: packet buffer too small, not all the packets will be sent.\n");
				break;
			}
*/
	if(!(srcIP.byte1 == 255 && srcIP.byte2 ==255 && srcIP.byte3 ==255))
	{//srcIP 排除不可能成为网段的地址
		sent = true;
		packet[38] = srcIP.byte1;
		packet[39] = srcIP.byte2;
		packet[40] = srcIP.byte3;
		for(k=1; k<255; k++)
		{//* 发送数据包
			packet[41] = k;
			/*		if (pcap_sendpacket(adhandle, packet, 42) != 0)
			{
			p->m_info = "Error sending the packet.";
			m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
			ipAutoConfig = false;
			return 0;
			}
			*/
			if (pcap_sendqueue_queue(squeue, pktheader, packet) == -1)
			{
				sent = false;
				printf("Warning: packet buffer too small, not all the packets will be sent.\n");
				break;
			}
		}
	}

	if(!(dstIP.byte1 == 255 && dstIP.byte2 ==255 && dstIP.byte3 ==255)
		&& !(dstIP.byte1 == srcIP.byte1 && dstIP.byte2 ==srcIP.byte2 && dstIP.byte3 ==srcIP.byte3))
	{//dstIP 排除不可能成为网段的地址并防止重复往同一网段发送
		sent = true;
		packet[38] = dstIP.byte1;
		packet[39] = dstIP.byte2;
		packet[40] = dstIP.byte3;
		for(k=1; k<255; k++)
		{//* 发送数据包
			packet[41] = k;
			/*	if (pcap_sendpacket(adhandle, packet, 42) != 0)
			{
			p->m_info = "Error sending the packet.";
			m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
			ipAutoConfig = false;
			return 0;
			}*/
			if (pcap_sendqueue_queue(squeue, pktheader, packet) == -1)
			{
				sent = false;
				printf("Warning: packet buffer too small, not all the packets will be sent.\n");
				break;
			}		
		}
	}
	if(!sent)
	{//两个网段都被排除
		p->m_info = "网关检测失败,重新检测.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		continue;
	}

	///////////////////////////
	//开始线程监听,设置监听过滤器	
	p->m_info = "正在检测IP冲突...";
	m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);

	packet_filter = "arp and dst host 127.0.0.1";//过滤规则
    if (pcap_compile(adhandle, &fcode, packet_filter, 1, netmask) <0 )
    {      
		p->m_info = "Unable to compile the packet filter. Check the syntax.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
        return 0;
    }    
    ////////////////set the filter
    if (pcap_setfilter(adhandle, &fcode)<0)
    {        
		p->m_info = "Error setting the filter.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
        return 0;
    }    
	///启动监听线程
	HANDLE hThread;//句柄跟ID 
	DWORD ThreadID; 
	hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)arpListenThread,adhandle,0,&ThreadID); 
//	AfxBeginThread(arpListenThread,adhandle);	
	///启动发送队列 
    if ((res = pcap_sendqueue_transmit(adhandle, squeue, sync)) < squeue->len)
    {
		char info[50];
		sprintf(info,"An error occurred sending the packets: %s. Only %d bytes were sent\n", pcap_geterr(adhandle), res);		
		p->m_info = info;
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
        return 0;       
    }
    //释放发送队列 
    pcap_sendqueue_destroy(squeue);

	///等待监听线程结数据包束,确保可以收到响应
	WaitForSingleObject(hThread,INFINITE);

	if(repIP_List.size() > 1)
		break;
	else if(repIP_List.size() == 1)
	{//如果反馈的ip唯一且是自己网卡的ip,则抛弃,重新开始
		if(repIP_List[0].byte1==(netip&0xff)
			&& repIP_List[0].byte2==(netip>>8&0xff)
			&& repIP_List[0].byte3==(netip>>16&0xff)
			&& repIP_List[0].byte4==(netip>>24&0xff))
			continue;
	}
	else if(repIP_List.size() == 0)
	{
		p->m_info = "网关检测失败,重新检测.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		continue;
	}
}//dectecttimes 循环结束
	
	pcap_close(adhandle);
	if(detectTimes==0)
	{
		p->m_info = "网关检测失败,请重新开始.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
		return 0;
	}
	
	

	///////////////////////////////////////////
	///若是响应的ip段不止一个,取数目多者。
	int srcCount=0, dstCount=0;
	int Index=-1, srcIndex=-1, dstIndex=-1;
	bool srcOwnPos[255],dstOwnPos[255];//已经占用的IP。从一开始,对应ip
	bool* ownPos;
	
	for(int k=0; k<255; k++)
	{
		srcOwnPos[k] = false;
		dstOwnPos[k] = false;
	}

	for(k=0; k<repIP_List.size(); k++)
	{
		if(repIP_List[k].byte1==(netip&0xff)
			&& repIP_List[k].byte2==(netip>>8&0xff)
			&& repIP_List[k].byte3==(netip>>16&0xff)
			&& repIP_List[k].byte4==(netip>>24&0xff))
			continue;//跳过获取的响应ip正好为自身网卡ip的情况

		if(repIP_List[k].byte1 == srcIP.byte1
			&& repIP_List[k].byte2 == srcIP.byte2
			&& repIP_List[k].byte3 == srcIP.byte3)
		{
			srcCount++;
			srcIndex = k;
			srcOwnPos[repIP_List[k].byte4] = true;
		}
		else if(repIP_List[k].byte1 == dstIP.byte1
			&& repIP_List[k].byte2 == dstIP.byte2
			&& repIP_List[k].byte3 == dstIP.byte3)
		{
			dstCount++;
			dstIndex = k;
			dstOwnPos[repIP_List[k].byte4] = true;
		}
	}

	Index = srcCount>=dstCount?srcIndex:dstIndex;
	ownPos = srcCount>=dstCount?srcOwnPos:dstOwnPos;
	
	if(Index == -1)
	{
		p->m_info = "网关检测失败,请重新开始.";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		ipAutoConfig = false;
		return 0; 
	}
	/////////////////////////////
	///至此得到了已经占用的ip列表,选择剩余的ip进行设置
	p->m_info = "已经获取到可用IP,请按\"确定\"进行设置.";
	m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
	CString strIp,strGate;

	srand(clock());
	int j = rand()%253 + 2;//随机选择一个
	while(ownPos[j])
	{
		j++;
		if(j==255)
			j = 2;
	}	

	//假设1为网关,考虑改进	
	strGate.Format("%d.%d.%d.%d",repIP_List[Index].byte1,repIP_List[Index].byte2,repIP_List[Index].byte3,1);//假设1为网关,考虑改进
	strIp.Format("%d.%d.%d.%d",repIP_List[Index].byte1,repIP_List[Index].byte2,repIP_List[Index].byte3,j);
	
	if(IDCANCEL == AfxMessageBox("系统分配给您的IP地址是:" + strIp + "。确定设置?" ,MB_OKCANCEL))
	{ 
		p->m_info = "";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
		netshInUse = false;
		ipAutoConfig = false;
		return 0;
	}
	
	///////////开始配置
	while(netshInUse)
		Sleep(400);
	netshInUse = true;
	//ip&mask&gateWay
    CString strCommand=sysDir+"/netsh interface ip set address name=\"本地连接\" static ";
	strCommand+=strIp;
	strCommand+=" mask = 255.255.255.0 gateway= ";
	strCommand+=strGate;
	strCommand+=" gwmetric = 1";
	strChar=strCommand.GetBuffer(strCommand.GetLength());
	strCommand.ReleaseBuffer();
	if (!CreatePipe(&hRead,&hWrite,&sa,0)) {//创建管道
		AfxMessageBox("Error On CreatePipe()");
		netshInUse = false;
		ipAutoConfig = false;
		return 0;
		} 
	si.hStdError = hWrite;          
	si.hStdOutput = hWrite;
	p->m_info = "正在配置IP(网关)地址..";
	m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
	if (!CreateProcess(NULL,strChar
		,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi)) {
		AfxMessageBox("Error on CreateProcess()");
		netshInUse = false;
		ipAutoConfig = false;		
		return 0;
	}
	CloseHandle(hWrite);//读管道前必须关掉写句柄

	while (true) {//读取管道信息(即netsh返回信息)
		if (ReadFile(hRead,buffer,4095,&bytesRead,NULL) == NULL)
          break;
		buffer[bytesRead] = 0; 
		s+=buffer;
		//    Sleep(200); 
	} 
	s.TrimRight();
	if(s!="确定。" && s!="" && s!="Ok.")
		::AfxMessageBox(s);
	else
	{
		p->m_info = "IP(网关)地址配置成功!";
		m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
	}
	//////////////////////                     dns1
	strCommand=sysDir+"/netsh interface ip set dns name = \"本地连接\" source=static addr= ";
	strCommand+="202.119.32.6";
	strChar=strCommand.GetBuffer(strCommand.GetLength());
	strCommand.ReleaseBuffer();
	if (!CreatePipe(&hRead,&hWrite,&sa,0)) {//重新创建管道
		AfxMessageBox("Error On CreatePipe()");
		netshInUse = false;
		ipAutoConfig = false;
		return 0;
	} 
	si.hStdError = hWrite; 
	si.hStdOutput = hWrite;
	p->m_info = "正在配置首选DNS服务器....";
	m_MWnd->SendMessage(WM_UPDATEDATA, FALSE);
	if (!CreateProcess(NULL,strChar
		,NULL,NULL,TRUE,NULL,NULL,NULL,&si,&pi)) {
		AfxMessageBox("Error on CreateProcess()");
		netshInUse = false;
		ipAutoConfig = false;

⌨️ 快捷键说明

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