最近在做信息安全導論的實驗,實驗很簡單,就是實現一個ping程序,能夠掃描主機是否打開的情況,但是,我也就納了悶了,每次有個不易發現的bug(可能由於自己知識有限造成的),都得讓我碰上,並且還得為這個bug操心好長時間,才能解決。。。。。。(抱怨是最浪費時間的一種行為!)ok~為了下次不再犯同樣的錯誤,現在先記錄一下吧~
我的錯誤是,在一切都准備好了的時候(初始化套接字,創建套接字,填充icmp數據頭,設定超時時間)發送數據包,成功,但是一旦接受就會超時,為此,我找了個能發送成功的程序,一步步的對照着看看哪兒不一樣,但是對照完了,發現一些核心代碼是一樣的。。。。。。這就蛋疼了,然后我就懷疑是不是因為這些小的錯誤導致的呢?我一步步的改正,首先我懷疑我的可能是因為發送和接受我放在了不同的函數里面,導致,在一個函數返回的時間里,接受函數錯過了接受(現在想想真是有病亂投醫啊。。。。。。。操作系統在端口應該有緩存的吧,在接受到數據后,會存到緩存里,然后才會調用。。。。。)我就把所有函合並成了一個函數,果不其然,沒有任何效果,依舊是發送超時。在經歷無盡的折磨以后,我發現我的變量全是定義在類里面的,而其他的程序就是在函數里面直接聲明,或者就是全局變量的,難道是因為類,半信半疑我就把所有定義在類里面的變量放到了cpp文件當中,作為全局變量調用,成功了~我去了!不應該啊,這是因為啥???在一個函數調用類里面的變量后,不改變內存中值?上課時不是這么講的啊。。。。。。苦逼的程序猿我,又一次發揮了初中時在物理課上學的控制變量法。。。。。。。。(我真丫該去學物理去。。。。。)一個一個的把變量弄回去,好了,終於發現了!!!!!!發送的數據包(實質是一個數組)沒有初始化,我擦了,為毛沒有初始化會出現這個情況?我發送時就是發送了icmp的頭部數據,跟其他數據沒有關系啊,在經過我控制變量法的不斷檢驗下,不管緩沖區有多大,只有初始化為0,后才可能接受成功,也就是被ping的主機識別。具體原因還有待進一步的研究。
下面來對原始套接字編程進行說明一下:
總體步驟是:
1、用管理員權限打開編輯器(vc6.0或vs)
2、創建ip和icmp的頭部數據結構
ip頭部數據結構:
typedef struct _iphdr //定義IP固定首部 { unsigned char h_lenver; //4位首部長度+4位IP版本號 unsigned char tos; //8位服務類型TOS unsigned short total_len; //16位總長度(字節) unsigned short ident; //16位標識 unsigned short frag_and_flags; //3位標志位 unsigned char ttl; //8位生存時間 TTL unsigned char proto; //8位協議 (TCP, UDP 或其他) unsigned short checksum;//16位IP首部校驗和 unsigned int sourceIP; //32位源IP地址 unsigned int destIP; //32位目的IP地址 }IpHeader;
icmp頭部數據結構:
typedef struct _icmphdr { unsigned char i_type; //類型 unsigned char i_code; //代碼 unsigned short i_cksum; //檢驗和 unsigned short i_id; //標識符 unsigned short i_seq; //序列號 unsigned long i_timestamp; //當前時間 =(unsigned long)::GetTickCount(); }IcmpHeader;
3、初始化原始套接字
WORD wVersionRequested = MAKEWORD(2, 2); WSADATA wsaData; if(WSAStartup(wVersionRequested, &wsaData) != 0) {
int flag = ::WSAGetLastError();
CString str;
str.Format(_T("%d"), flag);
MessageBox(_T("套接字初始化失敗!")+str);
}
4、聲明套接字
sRaw = ::socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
5、設置套接字選項(這里是不需要自己設置ip的簡單icmp程序)
int nTime = 1000; int ret = ::setsockopt(sRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&nTime, sizeof(nTime));
6、設置發送和接受的函數選項
SOCKADDR_IN dest;
dest.sin_family = AF_INET; dest.sin_port = htons(0); dest.sin_addr.S_un.S_addr = inet_addr(pBuf); /*設置要ping的主機ip(pBuf)*/
SOCKADDR_IN from;
int nLen = sizeof(from);
6、發送並接受信息
for (int i = 0; i < 4; i++) { icmp->i_cksum = 0; icmp->i_seq = i; icmp->i_timestamp = ::GetTickCount(); icmp->i_id = ::GetCurrentProcessId(); icmp->i_cksum = cheNum((USHORT*)buff, sizeof(IcmpHeader)); nRet = ::sendto(sRaw, buff, sizeof(IcmpHeader), 0, (SOCKADDR *)&dest, sizeof(dest)); if(nRet == SOCKET_ERROR) { int flag = ::WSAGetLastError(); CString str; str.Format(_T("%d"), flag); MessageBox(_T(" sendto() failed: ")+str); } nRet = ::recvfrom(sRaw, recvBuff, 1024 , 0, (SOCKADDR *)&from, &nLen); if(nRet == SOCKET_ERROR) { if(::WSAGetLastError() == WSAETIMEDOUT) { MessageBox(_T(" timed out/n")); } }
int nTick = ::GetTickCount(); IcmpHeader* pRecvIcmp = (IcmpHeader*)(recvBuff + 20);
if(pRecvIcmp->i_type != 0) { CString str; str.Format(_T("%d"),pRecvIcmp->i_type); MessageBox(_T(" nonecho type"), str+_T("recvd")); } else if(pRecvIcmp->i_id != ::GetCurrentProcessId()) { MessageBox(_T(" someone else's packet!")); } else { m_state = _T("open");/*返回到edit控件中*/ UpdateData(false); } }
7、關閉並清理
closesocket(sRaw);
WSACleanup();