扫描子网中的地址+Pings模拟

来源:互联网 发布:free book 知乎 编辑:程序博客网 时间:2024/06/05 19:23

1. 计算子网内包含的所有的IP地址

// CalculateSubnet.cpp -- 计算指定子网内包含的所有的IP地址#include "Winsock2.h"#include <string>#include <ctype.h>#pragma comment(lib, "Ws2_32.lib")/** * bool IsValidIP(char* ip)  * 功能:判断指定的IP地址是否有效。 * 1. 字符串中必须包含3个符号"." * 2. 被符号"."分隔的字符串必须小于或等于3 * 3. 被符号"."分隔的字符串必须可以转换成不大于255的整数。 **/bool IsValidIP(char* ip){std::string sip = ip;for (int judgeTimes = 1; judgeTimes <= 4; ++judgeTimes){int pos = (int)sip.find_first_of("."); // 找下一个句点if(0 == pos){return false;}std::string subip = sip.substr(0, pos);// 将ip分割为左边有效的地方sip = sip.substr(pos+1, sip.length() - pos );// 将ip分割为右边有效的地方if(subip.length() > 3){// 长度必须不大于3return false;}for(int i=0; i< (int)subip.length(); i++){// 检查是否满足全为数字的条件if(!isdigit(subip[i])){return false;}}int a = atoi(subip.c_str());// 检查数字是否在取值范围内if(1 == judgeTimes && (a < 1 || a > 255)){return false;}else if((2 == judgeTimes || 3 == judgeTimes) && a > 255){return false;}else if(4 == judgeTimes && a > 254){return false;}}return true;}int main(){const int argc = 3;    char * argv[] = {"", "192.168.0.1", "255.255.255.0"};if(argc != 3){printf("Usage: CalculateSubnet netaddr netmask\r\nExample: CalculateSubnet 192.168.0.0 255.255.255.0\n");return 1;}if(!IsValidIP(argv[1])){printf("%s is not a valid ip.\n", argv[1]);return 1;}if(!IsValidIP(argv[2])){printf("%s is not a valid ip.\n", argv[2]);return 1;}printf("计算指定子网内包含的所有的IP地址\n");printf("netaddr: %s\n", argv[1]);printf("netmask: %s\n", argv[2]);unsigned long lnetaddr = ntohl(inet_addr(argv[1]));// IP地址unsigned long lnetmask = ntohl(inet_addr(argv[2])); // 子网掩码unsigned long l_first_netaddr = lnetaddr & lnetmask;// 子网地址=IP地址&子网掩码unsigned long l_broadcast = lnetaddr | ~lnetmask;       // 广播地址=IP地址|~子网掩码// 计算子网中包含有效IP地址的数量long num = l_broadcast - l_first_netaddr - 1;printf("Number of valid IPs: %d\n\n", num);printf("IPs in subnet: \n=============\n");for(unsigned long i = l_first_netaddr+1; i < l_broadcast; i++){in_addr IPAddr;IPAddr.S_un.S_addr = ntohl(i);printf("%s\n", inet_ntoa(IPAddr));}system("pause");return 0;}/** 运行结果:计算指定子网内包含的所有的IP地址netaddr: 192.168.0.1netmask: 255.255.255.0Number of valid IPs: 254IPs in subnet:=============192.168.0.1192.168.0.2192.168.0.3192.168.0.4192.168.0.5192.168.0.6192.168.0.7192.168.0.8192.168.0.9192.168.0.10192.168.0.11192.168.0.12192.168.0.13192.168.0.14192.168.0.15192.168.0.16192.168.0.17192.168.0.18192.168.0.19192.168.0.20192.168.0.21192.168.0.22192.168.0.23192.168.0.24192.168.0.25192.168.0.26192.168.0.27192.168.0.28192.168.0.29192.168.0.30192.168.0.31192.168.0.32192.168.0.33192.168.0.34192.168.0.35192.168.0.36192.168.0.37192.168.0.38192.168.0.39192.168.0.40192.168.0.41192.168.0.42192.168.0.43192.168.0.44192.168.0.45192.168.0.46192.168.0.47192.168.0.48192.168.0.49192.168.0.50192.168.0.51192.168.0.52192.168.0.53192.168.0.54192.168.0.55192.168.0.56192.168.0.57192.168.0.58192.168.0.59192.168.0.60192.168.0.61192.168.0.62192.168.0.63192.168.0.64192.168.0.65192.168.0.66192.168.0.67192.168.0.68192.168.0.69192.168.0.70192.168.0.71192.168.0.72192.168.0.73192.168.0.74192.168.0.75192.168.0.76192.168.0.77192.168.0.78192.168.0.79192.168.0.80192.168.0.81192.168.0.82192.168.0.83192.168.0.84192.168.0.85192.168.0.86192.168.0.87192.168.0.88192.168.0.89192.168.0.90192.168.0.91192.168.0.92192.168.0.93192.168.0.94192.168.0.95192.168.0.96192.168.0.97192.168.0.98192.168.0.99192.168.0.100192.168.0.101192.168.0.102192.168.0.103192.168.0.104192.168.0.105192.168.0.106192.168.0.107192.168.0.108192.168.0.109192.168.0.110192.168.0.111192.168.0.112192.168.0.113192.168.0.114192.168.0.115192.168.0.116192.168.0.117192.168.0.118192.168.0.119192.168.0.120192.168.0.121192.168.0.122192.168.0.123192.168.0.124192.168.0.125192.168.0.126192.168.0.127192.168.0.128192.168.0.129192.168.0.130192.168.0.131192.168.0.132192.168.0.133192.168.0.134192.168.0.135192.168.0.136192.168.0.137192.168.0.138192.168.0.139192.168.0.140192.168.0.141192.168.0.142192.168.0.143192.168.0.144192.168.0.145192.168.0.146192.168.0.147192.168.0.148192.168.0.149192.168.0.150192.168.0.151192.168.0.152192.168.0.153192.168.0.154192.168.0.155192.168.0.156192.168.0.157192.168.0.158192.168.0.159192.168.0.160192.168.0.161192.168.0.162192.168.0.163192.168.0.164192.168.0.165192.168.0.166192.168.0.167192.168.0.168192.168.0.169192.168.0.170192.168.0.171192.168.0.172192.168.0.173192.168.0.174192.168.0.175192.168.0.176192.168.0.177192.168.0.178192.168.0.179192.168.0.180192.168.0.181192.168.0.182192.168.0.183192.168.0.184192.168.0.185192.168.0.186192.168.0.187192.168.0.188192.168.0.189192.168.0.190192.168.0.191192.168.0.192192.168.0.193192.168.0.194192.168.0.195192.168.0.196192.168.0.197192.168.0.198192.168.0.199192.168.0.200192.168.0.201192.168.0.202192.168.0.203192.168.0.204192.168.0.205192.168.0.206192.168.0.207192.168.0.208192.168.0.209192.168.0.210192.168.0.211192.168.0.212192.168.0.213192.168.0.214192.168.0.215192.168.0.216192.168.0.217192.168.0.218192.168.0.219192.168.0.220192.168.0.221192.168.0.222192.168.0.223192.168.0.224192.168.0.225192.168.0.226192.168.0.227192.168.0.228192.168.0.229192.168.0.230192.168.0.231192.168.0.232192.168.0.233192.168.0.234192.168.0.235192.168.0.236192.168.0.237192.168.0.238192.168.0.239192.168.0.240192.168.0.241192.168.0.242192.168.0.243192.168.0.244192.168.0.245192.168.0.246192.168.0.247192.168.0.248192.168.0.249192.168.0.250192.168.0.251192.168.0.252192.168.0.253192.168.0.254请按任意键继续. . .**/


 

2. 实现ping功能扫描子网

// scanAllSubnetIPAddress.cpp -- 实现ping功能扫描子网#include <string>#include <map>#include <list>#include <winsock2.h>#pragma comment(lib, "Ws2_32.lib")using namespace std;const int ICMP_MIN = 8;// ICMP包的最小长度为8个字节,只包含包头const int DEF_PACKET_SIZE = 32;// 执行ping操作时指定发送数据包的缺省大小const int MAX_PACKET = 1024;// 执行ping操作时指定发送数据包的最大大小const int ICMP_ECHO = 8;// 表示ICMP包为回射请求包const int ICMP_ECHOREPLY = 0;// 表示ICMP包为回射应答包// IP数据包头结构typedef struct iphdr {unsigned int h_len:4;// 包头长度unsigned int version:4;// IP协议版本unsigned char tos;// 服务类型(TOS)unsigned short total_len;// 包的总长度unsigned short ident;// 包的唯一标识unsigned short frag_and_flags;// 标识unsigned char ttl;// 生存时间(TTL)unsigned char proto;// 传输协议 (TCP, UDP等)unsigned short checksum;// IP校验和unsigned int sourceIP;unsigned int destIP;}IpHeader;// 执行ping操作时,定义发送IP数据包中包含的ICMP数据头结构typedef struct _ihdr {BYTE i_type;// 类型BYTE i_code;// 编码USHORT i_cksum;// 检验和USHORT i_id;// 编号USHORT i_seq;// 序列号ULONG timestamp;    // 时间戳}IcmpHeader;// 用于描述要执行ping操作的结构体struct PingPair{unsigned long ip;// 执行ping操作的IP地址LARGE_INTEGER starttime;// ping操作的开始时间LARGE_INTEGER endtime;// ping操作的结束时间bool flag;// 表示当前IP地址是否在线int period;// ping操作的用时PingPair(): ip(0), flag(false), period(-1){}PingPair(int ipp): ip(ipp), flag(false), period(-1){}};// 用于发送ICMP包的线程结构struct ThreadStruct{std::map<unsigned long, PingPair*> *ips;// 要执行ping操作的IP地址映射表SOCKET s;// 执行ping操作所使用的套接字int  timeout;// ping超时时间DWORD  tid;// 线程IDbool *sendCompleted;// 标识是否完成批量ping操作};/** * 各函数解释 * fill_icmp_data     填充ICMP请求包。 * checksum           计算ICMP校验和。 * decode_resp        对返回的IP数据包进行解码,定位到ICMP数据 * SendIcmp           使用指定的Socket向指定的单个IP地址发送ICMP请求包 * RecvIcmp           接收一个ICMP回应包 * RecvThreadProc     批量接收ICMP回应包的线程函数 * CreateSocket       创建Socket * DestroySocket      释放Socket * pings              启动多线程对一组地址执行ping操作 * FillSubnet         计算指定子网中包含的所有IP地址列表。 * ScanSubnet         扫描子网中所有的IP地址,返回在线的IP地址。 **/// 填充ICMP请求包void fill_icmp_data(char * icmp_data, int datasize){IcmpHeader *icmp_hdr;char *datapart;// 将缓冲区转换为icmp_hdr结构icmp_hdr = (IcmpHeader*)icmp_data;// 填充各字段的值icmp_hdr->i_type = ICMP_ECHO;// 将类型设置为ICMP响应包icmp_hdr->i_code = 0;// 将编码设置为0icmp_hdr->i_id = (USHORT)GetCurrentThreadId();// 将编号设置为当前线程的编号icmp_hdr->i_cksum = 0;// 将校验和设置为0icmp_hdr->i_seq = 0;// 将序列号设置为0datapart = icmp_data + sizeof(IcmpHeader);// 定义到数据部分memset(datapart,'E', datasize - sizeof(IcmpHeader));// 在数据部分随便填充一些数据return;}// 创建套接字SOCKET CreateSocket(DWORD timeout){WSADATA wsaData;SOCKET sockRaw = NULL;// 初始化if (WSAStartup(MAKEWORD(2,1),&wsaData) != 0){return sockRaw;}// 创建原始套接字sockRaw = WSASocket (AF_INET, SOCK_RAW,IPPROTO_ICMP, NULL, 0,WSA_FLAG_OVERLAPPED);if (sockRaw == INVALID_SOCKET) {return sockRaw;// WSASocket 错误}// 设置接收超时时间setsockopt(sockRaw,SOL_SOCKET,SO_RCVTIMEO,(char*)&timeout,sizeof(timeout));// 设置发送超时时间setsockopt(sockRaw,SOL_SOCKET,SO_SNDTIMEO,(char*)&timeout,sizeof(timeout));return sockRaw;}// 释放套接字void DestroySocket(SOCKET sockRaw){closesocket(sockRaw);WSACleanup();}// 计算ICMP包的校验和USHORT checksum(USHORT *buffer, int size){unsigned long cksum=0;// 把缓冲区中的数据相加while(size > 1) {cksum += *buffer++;size -= sizeof(USHORT);}if(size ) {cksum += *(UCHAR*)buffer;}cksum = (cksum >> 16) + (cksum & 0xffff);cksum += (cksum >> 16);return (USHORT)(~cksum);}// 使用指定的套接字向指定的单个IP地址发送ICMP请求包// 参数sockRaw,指定发送ICMP请求包的套接字// 参数ip,指定发送ICMP请求包的目标地址bool SendIcmp(SOCKET sockRaw, unsigned long ip){struct sockaddr_in dest,from;// 保存目标地址和源地址int datasize;// 指定ICMP数据包的大小int fromlen = sizeof(from);// 源地址长度unsigned long addr=0;// 保存主机字节序IP地址USHORT seq_no = 0;// 指定当前ICMP数据包的序号int ret = -1;// 保存函数的返回值// 将ip转换为dest,以便执行ping操作memset(&dest,0,sizeof(dest));addr = ntohl(ip);dest.sin_addr.s_addr = addr;dest.sin_family = AF_INET;// 设置ICMP数据包的大小datasize = DEF_PACKET_SIZE;datasize += sizeof(IcmpHeader);// 填充ICMP数据包char icmp_data[MAX_PACKET];memset(icmp_data,0,MAX_PACKET);fill_icmp_data(icmp_data,datasize);// 设置ICMP包头中的校验和、时间戳和序号((IcmpHeader*)icmp_data)->i_cksum = 0;((IcmpHeader*)icmp_data)->timestamp = GetTickCount();((IcmpHeader*)icmp_data)->i_seq = seq_no++;((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data,datasize);// 向dest发送ICMP数据包int bwrote;bwrote = sendto(sockRaw,icmp_data,datasize,0,(struct sockaddr*)&dest,sizeof(dest));// 发送失败,则返回falseif (bwrote == SOCKET_ERROR){if (WSAGetLastError() != WSAETIMEDOUT){ret = false; // 发送错误}}if (bwrote < datasize ) {return false;}//发送成功,返回truereturn true;}// 对返回的IP数据包进行解码,定位到ICMP数据// 因为ICMP数据包含在IP数据包中int decode_resp(char *buf, int bytes, struct sockaddr_in *from, DWORD tid){IpHeader *iphdr;// IP数据包头IcmpHeader *icmphdr;// ICMP包头unsigned short iphdrlen;// IP数据包头的长度iphdr = (IpHeader *)buf;// 从buf中IP数据包头的指针// 计算IP数据包头的长度iphdrlen = iphdr->h_len * 4 ; // number of 32-bit words *4 = bytes// 如果指定的缓冲区长度小于IP包头加上最小的ICMP包长度,则说明它包含的ICMP数据不完整,或者不包含ICMP数据if (bytes < iphdrlen + ICMP_MIN) {return -1; //printf("Too few bytes from %s\n",inet_ntoa(from->sin_addr));}// 定位到ICMP包头的起始位置icmphdr = (IcmpHeader*)(buf + iphdrlen);// 如果ICMP包的类型不是回应包,则不处理if (icmphdr->i_type != ICMP_ECHOREPLY) {return -2; //fprintf(stderr,"non-echo type %d recvd\n",icmphdr->i_type);}// 发送的ICMP包ID和接收到的ICMP包ID应该对应if (icmphdr->i_id != (USHORT)tid){ //(USHORT)GetCurrentProcessId()) {return -3; //fprintf(stderr,"someone else's packet!\n");}// 返回发送ICMP包和接收回应包的时间差int time = GetTickCount() - (icmphdr->timestamp);if(time >= 0)return time;elsereturn -4; // 时间值不对}// 接收一个ICMP回应包// 参数sockRaw指定接收ICMP回应包的套接字// 参数ip指定发送ICMP回应包的IP地址// 参数tid指定发送ICMP回应包的线程IDint RecvIcmp(SOCKET sockRaw, unsigned long *ip, DWORD tid){struct sockaddr_in from;// 接收到ICMP的来自的地址int fromlen = sizeof(from);    // 地址from的长度int bread;// 调用recvfrom()函数的返回结果char recvbuf[MAX_PACKET];// 用于接收ICMP回应我的缓冲区// 接收ICMP回应包bread = recvfrom(sockRaw,recvbuf,MAX_PACKET,0,(struct sockaddr*)&from,&fromlen);if (bread == SOCKET_ERROR){if (WSAGetLastError() == WSAETIMEDOUT) {return -1; // 超时}elsereturn -9; // 接收错误}// 对ICMP回应包进行解析int time = decode_resp(recvbuf,bread,&from, tid);// 如果可以ping通,则返回发送ICMP请求包到接收ICMP回应包的时间,否则返回-1if( time >= 0 ){*ip = ntohl(from.sin_addr.S_un.S_addr);return time;}else{return -1;}}// 接收ICMP回应包的线程DWORD WINAPI RecvThreadProc(void *param){int count = 0;ThreadStruct *unionStruct = (ThreadStruct *)param;// 参数为ThreadStruct,即线程数量DWORD startTime = GetTickCount();// 记录开始时间DWORD timeout = unionStruct->timeout * 2;// 超时时间加长SOCKET sockRaw = unionStruct->s;// 设置套接字std::map<unsigned long, PingPair*> *ips = unionStruct->ips;// 设置要执行批量ping操作的IP地址DWORD tid = unionStruct->tid;// 设置线程ID// 如果批量ping操作未完成,并且没有超时,则调用RecvIcmp()函数接收一个IP地址的响应包std::map<unsigned long, PingPair*>::iterator itr;while( !*(unionStruct->sendCompleted) || GetTickCount() - startTime < timeout){unsigned long ip;int ret = RecvIcmp(sockRaw, &ip, tid);// 接收一个IP的ICMP响应包if(ret < 0){continue;}if( (itr = ips->find(ip)) != ips->end() && !itr->second->flag){QueryPerformanceCounter( &(itr->second->endtime) );// 获取结束时间itr->second->flag = true;// 设置在线标识}}return 0;}// ping一组设备int pings(std::map<unsigned long, PingPair*> &ips, DWORD timeout){SOCKET s = CreateSocket(timeout);// 创建ping操作使用的套接字if(s == INVALID_SOCKET){// 如果创建失败,则返回return -1;}// 准备执行批量ping操作的TThreadStruct unionStruct;unionStruct.ips = &ips;// 要执行ping操作的IP地址映射表unionStruct.s = s;    // 发送和接收ICMP数据包的套接字unionStruct.timeout = timeout;    // 超时时间unionStruct.tid = GetCurrentThreadId();// 线程IDunionStruct.sendCompleted = new bool(false);// 标识为未发送完成// 创建批量ping操作线程,线程函数为RecvThreadProc,参数为unionStructDWORD tid;HANDLE handle = CreateThread(NULL, 0, RecvThreadProc, &unionStruct, 0, &tid);// 依次向ips中所有IP地址发送std::map<unsigned long, PingPair*>::iterator itr;for(itr = ips.begin();itr != ips.end();itr++){SendIcmp(s, itr->first);// 发送ICMP请求包QueryPerformanceCounter( &itr->second->starttime );// 记录初始时间Sleep(10);}// 因为ICMP是基于不可靠的UDP协议的// 为了防止目标IP没有收到ICMP请求包,这里再发送一次for(itr = ips.begin(); itr != ips.end(); itr++){SendIcmp(s, itr->first); // 发出所有数据Sleep(10);}// 将发送完成标识设置为true*(unionStruct.sendCompleted) = true;// 等待接收线程返回DWORD ret = WaitForSingleObject(handle, timeout * 3);// 结束线程if(ret == WAIT_TIMEOUT){printf("Kill Thread\n");TerminateThread(handle, 0);}CloseHandle(handle);// 关闭线程句柄DestroySocket(s);// 释放套接字// 获取CPU每秒钟跑几个ticksLARGE_INTEGER ticksPerSecond;QueryPerformanceFrequency(&ticksPerSecond);// 依次对所有IP地址进行处理for(itr = ips.begin();itr != ips.end();itr++){// 如果在线(flag=true),则记录执行ping操作的时间if(itr->second->flag == true){double elapsed = ((double)(itr->second->endtime.QuadPart - itr->second->starttime.QuadPart) / ticksPerSecond.QuadPart); // 經過的時間, 依自己程式需求選擇精準度if(elapsed <= 0)elapsed = 0;itr->second->period = (int)(elapsed*1000);}}delete unionStruct.sendCompleted;return 0;}// 计算子网地址为NetAddr和子网掩码为NetMask的子网中包含的所有IP地址列表list<string> FillSubnet(string NetAddr, string NetMask){list<string> IpList;// 将网络地址和子网掩码从网络字节顺序转换为主机字节顺序unsigned long _inetaddr = ntohl(inet_addr(NetAddr.c_str()));unsigned long _inetmask = ntohl(inet_addr(NetMask.c_str()));// 计算网络地址和广播地址unsigned long first_netaddr = _inetaddr & _inetmask;unsigned long broadcast = _inetaddr | ~_inetmask;// 计算子网中包含有效IP地址的数量long num = broadcast - first_netaddr - 1;for(unsigned long i=first_netaddr+1; i<broadcast; i++){// 保存IP地址的结构体in_addr IPAddr;IPAddr.S_un.S_addr = ntohl(i);// 添加每个IP地址到IpList中IpList.push_back(inet_ntoa(IPAddr));}return IpList;}list<string> ScanSubnet(string NetAddr, string NetMask, DWORD timeOut){// 计算子网中的IP地址列表list<string> IpList = FillSubnet(NetAddr, NetMask);// 将IpList转换成用来执行ping操作的所有IP地址ipAllstd::map<unsigned long, PingPair*> ipAll;list<string>::iterator IpItr;// 将IpList转换为ipAll,为调用pings()函数准备数据for(IpItr = IpList.begin(); IpItr != IpList.end(); IpItr++){string ip = *IpItr;if(ip.empty())// 如果设备IP地址为空,则不处理continue;unsigned int uip = ntohl(inet_addr(ip.c_str()));PingPair *p = new PingPair(uip);ipAll[uip] = p;}// 执行批量ping操作pings(ipAll, timeOut);// 将活动IP地址保存在ActiveIpList中list<string> ActiveIpList;std::map<unsigned long, PingPair*>::iterator ipItr;for(ipItr=ipAll.begin();ipItr!=ipAll.end();ipItr++){if(ipItr->second->flag){in_addr IPAddr;IPAddr.S_un.S_addr = ntohl(ipItr->second->ip);// 将活动IP地址保存在ActiveIpList中ActiveIpList.push_back(inet_ntoa(IPAddr));}delete ipItr->second;}return ActiveIpList;}int main(){    const int argc = 3;    char * argv[] = {"", "192.168.0.1", "255.255.255.0"};if(argc != 3){printf("参数数量不正确。请指定要ping的子网的子网地址和子网掩码。\n");return -1;}// 执行ping操作printf("ping subnet: %s, %s...\n", argv[1], argv[2]);// 扫描子网list<string> ActiveIpList = ScanSubnet(argv[1], argv[2], 500);// 显示所有在线IP地址printf("The Active IP Address is below:\n");list<string>::iterator IpItr;for(IpItr = ActiveIpList.begin(); IpItr != ActiveIpList.end(); IpItr++){string ip = *IpItr;if(ip.empty())// 如果设备IP地址为空,则不处理continue;printf("%s\n", ip.c_str());}system("pause");return 0;}


 

 

原创粉丝点击