1. 程式人生 > >http 重定向 302報文

http 重定向 302報文

我這裡的重定向環境是對客戶端的請求直接截斷重定向到其他頁面。

以太頭和Ip頭想應資料對調。

Tcp頭flags標記和序號問題。主要問題

資料內容主要的就兩條:

"HTTP/1.1 302 Moved Temporarily"

"Location: 路徑"

一、以太頭結構


在幀結構裡我用到的有MAC目的地址,以太型別。至於802.1Q標籤,如果加了,對以太型別想對地址要向生移4個位元組。源來的地址值則為0x8100。

用到的以太型別是 0x800 表以後一個負載段的內容是一個IP包。這個巨集定義在if_ether.h標頭檔案裡。

#define ETH_P_PUPAT     0x0201              /*Xerox PUP Addr Trans packet      */

#define ETH_P_IP    0x0800              /*Internet Protocol packet     */

#define ETH_P_X25 0x0805              /* CCITTX.25                     */

#define ETH_P_ARP         0x0806              /*Address Resolution packet */

先看是不是IP包

         是:對換MAC地址,複製以太頭結構到buffer

         不是:這個就直接轉發的轉發,丟包的丟包了不管了。

 
         unsignedshort *port = NULL;
         unsignedshort h_poto = 0;
        
         if(pkt->eth_hdr_ptr->h_proto== htons(ETH_P_8021Q))
         {
                   h_poto= *((unsigned short *) pkt ->eth_hdr_ptr + 8);
         }
         else
         {
                   h_poto= pkt ->eth_hdr_ptr->h_proto;
         }
        
         if( h_poto != htons(ETH_P_IP) )
         {
                   returnERROR_FAIL;
         }
 
         port= (unsigned short *) pkt->l4_hdr_ptr;
         if(port== NULL)
         {
                   returnERROR_FAIL;
         }


二、IP報文頭結構的話

 

struct iphdr {
#if defined(__LITTLE_ENDIAN_BITFIELD)
         __u8         ihl:4,
                   version:4;
#elif defined (__BIG_ENDIAN_BITFIELD)
         __u8         version:4,
                ihl:4;
#else
#error       "Pleasefix <asm/byteorder.h>"
#endif
         __u8         tos;
         __be16     tot_len;
         __be16     id;
         __be16     frag_off;
         __u8         ttl;
         __u8         protocol;
         __sum16 check;
         __be32     saddr;
         __be32     daddr;
         /*Theoptions start here. */
};
 


Version = 4;

Ihl 一般情況下都是5。我在重定向的時候直接 new_ipv4_hdr->ihl  = old_ipv4_hdr->ihl;因為長度使用收到報文頭長度,所以如果後還有選項的話是直接複製到新的頭結構中。

Tos兩樣複製接收到的報文資料。現在都不怎麼用這個欄位了。

Tot_len是指從IP頭結構到資料的總長度,自己算。我在算你在算,在校驗之前算好。

Id我比較簡單直接用0。這個會用於分片和重新組裝資料報。我也沒用到只是發一個302重定向報文。

Frag_off直接0。我也沒有分片,用不到偏移。

Ttl一般不是32就是64。

Protoco 根據自己的實際情況為6。

Check 去網上找個現成的就可以。也可以找找lib庫介面函式

Saddr daddr 直接對換了就可以。

要是有大小端的情況下,在這些頭結構中如果欄位是32位的就用 htonl 或是 ntohl;要是16位的話就用 htons 或是 ntohs。這個問題當時還糾結了好幾天。唉

 
int reply_packet_iphdr_data(pkt_info_s *pkt,struct iphdr *ipv4_hdr)
{
         //其他沒有填寫欄位的內容需要在算校驗之前填好
         ipv4_hdr->id=0;
         ipv4_hdr->version=4;
         ipv4_hdr->ihl=pkt->ipv4_hdr_ptr->ihl;
         ipv4_hdr->ttl=64;
         ipv4_hdr->protocol=6;      //tcp 6
         ipv4_hdr->frag_off=ntohs(0x4000);
         ipv4_hdr->tos=pkt ->ipv4_hdr_ptr->tos;
 
         ipv4_hdr->daddr=pkt ->ipv4_hdr_ptr->saddr;
         ipv4_hdr->saddr=pkt ->ipv4_hdr_ptr->daddr;
         ipv4_hdr->check= 0;
 
         returnERROR_OK;
}


 三、tcp頭結構


struct tcphdr {
    __be16 source;
    __be16 dest;
    __be32 seq;
    __be32 ack_seq;
#if defined(__LITTLE_ENDIAN_BITFIELD)
    __u16  res1:4,
        doff:4,
       fin:1,
       syn:1,
       rst:1,
       psh:1,
       ack:1,
       urg:1,
       ece:1,
       cwr:1;
#elif defined(__BIG_ENDIAN_BITFIELD)
    __u16  doff:4,
       res1:4,
       cwr:1,
       ece:1,
       urg:1,
       ack:1,
       psh:1,
       rst:1,
       syn:1,
       fin:1;
#else
#error "Adjustyour <asm/byteorder.h> defines"
#endif
    __be16 window;
    __sum16    check;
    __be16 urg_ptr;
};
 


TCP協議頭最少20個位元組,和IP頭到是一樣

TCP源埠(Source Port),TCP目的埠(Destination port) 對換

  TCP序列號(序列碼,Sequence Number):32位

TCP應答號(Acknowledgment Number):32位

資料偏移量(HLEN):4位包括TCP頭大小,指示何處資料開始。其實也是tcp頭部長度。

  保留(Reserved):6位值域,這些位必須是0。為了將來定義新的用途所保留。

標誌(Code Bits):6位標誌域:

1.URG:緊急標誌

緊急(The urgent pointer)標誌有效。緊急標誌置位,

2.ACK:確認標誌

確認編號(Acknowledgement Number)欄有效。大多數情況下該標誌位是置位的。TCP報頭內的確認編號欄內包含的確認編號(w+1,Figure:1)為下一個預期的序列編號,同時提示遠端系統已經成功接收所有資料。

3.PSH:推標誌

該標誌置位時,接收端不將該資料進行佇列處理,而是儘可能快將資料轉由應用處理。在處理telnet或rlogin等互動模式的連線時,該標誌總是置位的。

4.RST:復位標誌

復位標誌有效。用於復位相應的TCP連線。

5.SYN:同步標誌

同步序列編號(SynchronizeSequence Numbers)欄有效。該標誌僅在三次握手建立TCP連線時有效。它提示TCP連線的服務端檢查序列編號,該序列編號為TCP連線初始端(一般是客戶端)的初始序列編號。在這裡,可以把TCP序列編號看作是一個範圍從0到4,294,967,295的32位計數器。通過TCP連線交換的資料中每一個位元組都經過序列編號。在TCP報頭中的序列編號欄包括了TCP分段中第一個位元組的序列編號。

6.FIN:結束標誌

帶有該標誌置位的資料包用來結束一個TCP回話,但對應埠仍處於開放狀態,準備接收後續資料。

  視窗(Window):16位,用來表示想收到的每個TCP資料段的大小。隨便寫個就好不要太小了。

  校驗位(Checksum):16位TCP頭。源機器基於資料內容計算一個數值,收資訊機要與源機器數值 結果完全一樣,從而證明資料的有效性。

  優先指標(緊急,Urgent Pointer):16位,指向後面是優先資料的位元組,在URG標誌設定了時才有效。如果URG標誌沒有被設定,緊急域作為填充。加快處理標示為緊急的資料段。

選項(Option):長度不定,但長度必須以位元組。如果沒有 選項就表示這個一位元組的域等於0。 一般只用出到在syn包中

資料(Date):應用程式的資料。

從三次握手開始。

C -------> S:     syn 1; ack 0;客戶端seq,ack_seq會有值。可能是一個隨機值,寫一個就可以

C <------- S:      syn 1; ack 1; 捎帶ack。服務端回 seq = 隨機值。Ack_seq = old_seq + 1

C -------> S:     syn 0; ack 1; 端回 seq = old_ack_seq; Ack_seq= old_seq + 1

C -------> S: get psh1; ack 1; 端回seq  Ack_seq 和上一個ack包的一樣。

C <------- S: 302 fin 1;psh 0; ack 1; 端回 seq= old_ack_seq; Ack_seq = old_seq + data_len

對於客戶端發過來的seq和ack_seq 值直接取就可以,為了給回包用。之後就直接丟了。

在頭兩個syn包裡會有選項欄位內容。要注意MSS

重定向主要就回了兩個包:一個是syn_ack包。另一個是http 302包。

注意get 報文可能會進行tcp分片:發兩個ack資料包等一個ack確認回包。真到psh,ack包發之後才算是一個完整的get 報文。

//這些資料基本不用變。
    tcp_hdr->dest= pdesc->tcp_hdr_ptr->source;
    tcp_hdr->source= pdesc->tcp_hdr_ptr->dest;
    tcp_hdr->doff= pdesc->tcp_hdr_ptr->doff;
    tcp_hdr->window= pdesc->tcp_hdr_ptr->window;
    tcp_hdr->check= 0;
    tcp_hdr->seq= pdesc->tcp_hdr_ptr->ack_seq;
    tcp_hdr->ack= 1;
 
// ack_seq欄位的兩種情況
    if(send_falg!= 0)
    {
        tcp_hdr->ack_seq= htonl(ntohl(pdesc->tcp_hdr_ptr->seq)+ data_len);
    }
    else
    {
        tcp_hdr->ack_seq= htonl(ntohl(pdesc->tcp_hdr_ptr->seq)+ 1);
    }


//剩下的就是根據下邊四個狀態進行處理了。

        fin:1,

        syn:1,

        rst:1,

        psh:1,

 四、http 302報文最小頭部

在組過302報文頭內容之後需要自己組http302報文內容 1,最小可用http 302報文頭 HTTP/1.1 302 Moved Temporarily
Server: JSP2/1.0.26
Location: www.hao123.com
這裡需要注意server: 的內容必須是location認識的伺服器型別,不知道的話可以去抓個包看下。要是不對的話302報文不認。 2,自組302報文不認, 在進行三次握手重定向時自組syn_ack報文復回正確,到了302報文客戶端接收了,wirshark抓包也沒錯但就是不認的情況,可以除錯下http302頭部欄位內容。 eg: 如果有Content-Length:欄位但是值不對,對於有的伺服器來就不認。這是我碰到的一個問題,除錯了好幾天MD。而且這個值還不怎麼好新增,直接幹掉OK。