記一次蛋疼的Raw socket發送經歷。附:Raw socket編程總結


最近在做信息安全導論的實驗,實驗很簡單,就是實現一個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();

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM