socket实现ping嗅探,获取局域网所有活动主机
2015-12-11 12:06
791 查看
大概思路是:获取本主机IP,然后将它和子网掩码进行与操作,可知道子网的主机号范围,然后逐个进行Ping,最多ping4次。
ping是基于icmp报文的,它被封装在ip中发送出去。
主要问题是,这样单线程Ping速度真的非常慢,所以我尝试了多线程,不过过程并不顺利,主要是在recvIcmp过程中,内部或许有些更复杂的步骤。
不过,有个简单的实现方法是通过system函数调用DOS指令ping,并将它的输出重定向,然后检索一下输出结果就好了,只是楼主比较执着于自己实现ping功能。
这个ping是我一个p2p局域网通信的子功能,接下来先写syn端口嗅探咯,回头再优化ping。
.完整的代码就不贴出来了,不过核心的就这些
先获取本主机IP,这个结构和winpcap很相似,用过的大哥应该都清楚。
发送ping请求,ip/icmp首部结构不知道的在百度搜一搜
ping是基于icmp报文的,它被封装在ip中发送出去。
主要问题是,这样单线程Ping速度真的非常慢,所以我尝试了多线程,不过过程并不顺利,主要是在recvIcmp过程中,内部或许有些更复杂的步骤。
不过,有个简单的实现方法是通过system函数调用DOS指令ping,并将它的输出重定向,然后检索一下输出结果就好了,只是楼主比较执着于自己实现ping功能。
这个ping是我一个p2p局域网通信的子功能,接下来先写syn端口嗅探咯,回头再优化ping。
.完整的代码就不贴出来了,不过核心的就这些
先获取本主机IP,这个结构和winpcap很相似,用过的大哥应该都清楚。
PIP_ADAPTER_INFO pAdapterInfo = nullptr; PIP_ADAPTER_INFO pCurrAdater = nullptr; // 假设设备只有一个 unsigned long adapterSum = sizeof(IP_ADAPTER_INFO); pAdapterInfo = (PIP_ADAPTER_INFO)malloc(sizeof(IP_ADAPTER_INFO)); if (pAdapterInfo == nullptr) { AfxMessageBox((LPCTSTR)"malloc failed, in const std::vector<in_addr>& CNetTrans::GetHostname()"); } // 如果获取过程返回ERROR_BUFFER_OVERFLOW, 说明分配的内存不够,这是adapterSum被修改为适合的大小 if (GetAdaptersInfo(pAdapterInfo, &adapterSum) == ERROR_BUFFER_OVERFLOW) { free(pAdapterInfo); pAdapterInfo = nullptr; // 分配合适的内存 pAdapterInfo = (PIP_ADAPTER_INFO)malloc(adapterSum); if (pAdapterInfo == nullptr) { AfxMessageBox((LPCTSTR)"malloc failed, in CNetTrans::GetHostname()"); } } char description[256]; char ip[16]; char netmask[16]; char gate[16]; if (GetAdaptersInfo(pAdapterInfo, &adapterSum) != NO_ERROR) { AfxMessageBox((LPCTSTR)"GetAdapterInfo error, in CNetTrans::GetHostname()"); } pCurrAdater = pAdapterInfo; while (pCurrAdater) { memset(description, 0, sizeof(description)); memset(gate, 0, sizeof(gate)); memset(ip, 0, sizeof(ip)); memset(netmask, 0, sizeof(netmask)); // 获取设备描述,IP,子网掩码 strncpy(description, pCurrAdater->Description, strlen(description)); strncpy(ip, pCurrAdater->IpAddressList.IpAddress.String, sizeof(ip)); strncpy(netmask, pCurrAdater->IpAddressList.IpMask.String, sizeof(netmask)); strncpy(gate, pCurrAdater->GatewayList.IpAddress.String, sizeof(gate)); pCurrAdater = pCurrAdater->Next; // 过滤很多没用的设备,即没有IP地址,用于一些window系统内部的功能 // 还有安装了虚拟机的也会有,一起过滤 if (strcmp(ip, "0.0.0.0") == 0 || strcmp(gate, "0.0.0.0") == 0) continue; struct device one = {description, gate, ip, netmask}; CNetTrans::m_Host.push_back(one); }
发送ping请求,ip/icmp首部结构不知道的在百度搜一搜
SOCKET sock = INVALID_SOCKET; if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP)) == SOCKET_ERROR) { AfxMessageBox((LPCTSTR)"socket error, in CNetTrans::GetLanActiveUsers() "); } unsigned long nIp = inet_addr(dev.ip.c_str()); unsigned long nGate = inet_addr(dev.gate.c_str()); unsigned long nNetmask = inet_addr(dev.netmask.c_str()); int subNetHostSum = ((~nNetmask) >> 24) - 1; // 减去主机号为全0 和 全1的地址 // 计算子网掩码中Bit为1的个数 unsigned long check = 1; int oneCount = 0; for (int k = 0; k < 32; ++k) { if (check & nNetmask) oneCount++; check = check << 1; } /////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////单线程,Ping局域网//////////////////////////////// struct sockaddr_in addr; addr.sin_family = AF_INET; addr.sin_port = 0; addr.sin_addr.s_addr = (nIp & nNetmask) + (1 << oneCount); int addrlen = sizeof(struct sockaddr_in); for (int index = 0; index < subNetHostSum; ++index) { // 忽略本主机 和 网关 if (addr.sin_addr.s_addr == nGate || addr.sin_addr.s_addr == nIp) { addr.sin_addr.s_addr = addr.sin_addr.s_addr + (1 << oneCount); continue; } for (int count1 = 0; count1 < 4; ++count1) { if (SendIcmp(sock, addr, addrlen) == false) continue; int ret = RecvIcmp(sock, addr, addrlen); if (ret == -1) // 请求超时 或者 select 调用出错 continue; else if (ret == -2) { count1--; continue; } m_LanUsers.m_ActiveHost.push_back(addr.sin_addr.s_addr); char *c = (char *)malloc(16); strncpy(c, inet_ntoa(addr.sin_addr), 16); m_test.push_back(c); break; } addr.sin_addr.s_addr = addr.sin_addr.s_addr + (1 << oneCount); } closesocket(sock);
bool CNetTrans::SendIcmp(SOCKET sock, sockaddr_in addr, int addrlen) { icmp_req iHdr; iHdr.icmphdr.type = ICMP_REQ; iHdr.icmphdr.code = 0; iHdr.icmphdr.checksum = 0; // RFC说这个一般为0,不知道有什么用 iHdr.icmphdr.flag = 0; iHdr.icmphdr.seque = 0; for (size_t index = 0; index < sizeof(iHdr.data); ++index) { iHdr.data[index] = '1' + index; } // 检验和一定要计算 iHdr.icmphdr.checksum = this->checksum((u_short *)&iHdr, sizeof(icmp_req)); int nRet = sendto(sock, (char *)&iHdr, sizeof(icmp_req), 0, (struct sockaddr *)&addr, addrlen); if (nRet == SOCKET_ERROR) { //AfxMessageBox((LPCTSTR)"sendto error, in CNetTrans::SendIcmp(SOCKET sock, const sockaddr_in &pAddr, int addrlen)"); return false; } return true; } int CNetTrans::RecvIcmp(SOCKET sock, sockaddr_in addr, int addrlen) { /* 2015/12/9/23:11 线程的执行存在问题,如当192.168.1.x正确收到回复报文后 因为发送了四个报文,所以回复也可能是多个,后面连续的IP竟然会recvfrom得到x的回复数据包 究竟在创建套接字 还是 recvfrom 调用有问题? 下面copy备份作判断。 */ struct sockaddr_in a = addr; char buf[128]; int pLen = addrlen; fd_set rdSet; struct timeval time; rdSet.fd_count = 1; rdSet.fd_array[0] = sock; time.tv_sec = 1; //微妙 /* 2015/12/9/20:40 用单线程ping测试,发现提高超时的时间长度可提高准确率,比如1秒 太小的超时时间会导致得到多余的错误的IP,但按照不可达的情况来说,按照这个函数 实现的逻辑来说,明显是直接忽略的,为什么会导致返回1??? 不可达的时候本主机会回复一个icmp报文给自己。 */ time.tv_usec = 0; /* 2015/12/8/18:55 这里有个问题:由于目标主机不可达的情况下,回复报文的时间过长,使得和请求超时分辨不出来, 导致icmp网络嗅探过程时间长。 目前想到的解决办法,用多线程。 */ int iRet = select(sock + 1, &rdSet, nullptr, nullptr, &time); // 请求超时 if (iRet == 0 || iRet == SOCKET_ERROR) { if (iRet == SOCKET_ERROR) return -2; return -1; } /* 这里有个问题:有时recvfrom阻塞不往下执行,通过select设定一个定时器可以解决。 发生请求超时时,那是因为局域网的确有此地址的主机,路由表有他的表项,但主机并不在线, 这是并没有包被回复。 和目标主机不可达不同,他有包被回复,来自本主机ip,recvfrom可以感知。 */ if (iRet == 1) { int size = recvfrom(sock, buf, sizeof(buf), 0, (struct sockaddr *)&addr, &pLen); if (size == SOCKET_ERROR) { //MessageBox(buf,"");//"recvfrom error, in CNetTrans::RecvIcmp(SOCKET sock, const sockaddr_in &pAddr, int addrlen)")); return -2; } // 接受到的数据是IP层的 icmp_reply iReply; iReply.iphdr = (struct ip_hdr *)buf; int len = ((iReply.iphdr->ver4_hlen4 & 0xf)) * 4; iReply.icmphdr = (struct icmp_hdr *)(buf + len); struct in_addr a1; a1.s_addr = iReply.iphdr->souraddr; char *test = inet_ntoa(a1); unsigned long us = inet_addr(m_Host[0].netmask.c_str()) & inet_addr(m_Host[0].ip.c_str()); unsigned long you = iReply.iphdr->souraddr & inet_addr(m_Host[0].netmask.c_str()); unsigned long testip = inet_addr("192.168.1.107"); /* 2015/12/9/22:14 创建端口为0的icmp原始套接字相当于一个网络嗅探器?有接收到源地址不是本局域网的地址的情况 us == you 是针对这种情况的判断 2015/12/10/11:09 的确是的,所以才会出现一些局域网内奇怪的IP在包含,recvIcmp接收到非目的IP得回复, 但Thread_GetLanUsers线程函数不知道,以为是本局域网ip可以ping通。 */ if (addr.sin_addr.s_addr == inet_addr("192.168.1.107")) return -1; if (a.sin_addr.s_addr != addr.sin_addr.s_addr) return -2; if (iReply.icmphdr->type == ICMP_REPLY) return 1; } }
相关文章推荐
- 实现Web上的用户登录功能
- Mysql视图
- UITextView
- Oracle修改表空间大小的方法
- LeetCode:Excel Sheet Column Number
- NYOJ202 红黑树 【预购】
- A1490. osu!(乔明达)
- iOS应用架构谈(4) 本地持久化方案及动态部署
- ios--- UISegment去边框颜色
- Kafka producer程序本地运行时发送信息失败解决方案
- Android 分割合并文件 assets res/raw 存放资源的限制
- phpstorm移除已创建的项目
- Axure RP7.0制作Tab选项卡
- DataSet 中的数据排序 及 DataRow装成DataTable
- [LeetCode]Plus One
- C#和网页js互调代码
- 详解Linux系统中的文件名和文件种类以及文件权限
- 简单谈谈MySQL的loose index scan
- Key is invalid Fingerprint cannot be generated
- PHP漏洞全解(六)-Session劫持