您的位置:首页 > 理论基础 > 计算机网络

自己动手学TCP/IP–http协议(http报文格式)

2015-04-09 10:23 721 查看


HTTP请求报文

http请求数据包的格式:头部(request line + header)+  数据(data)

头部和数据包体通过一个空行来隔开,头部的格式主要包括请求行+请求头部。如下图




请求行

请求行由请求方法字段、URL字段和HTTP协议版本字段3个字段组成,它们用空格分隔如:

GET /index.html HTTP/1.1。

HTTP协议的请求方法有GET、POST、HEAD、PUT、DELETE、OPTIONS、TRACE、CONNECT。这里介绍最常用的GET方法和POST方法。

GET方式:在URL里面就说明要请求的资源,URL里面包含参数,“?”后面就是参数,而“?”前面就是URL的结束。“?ip=192.168.156.11&active=on”这种就是GET方式的包,而服务器把客户端请求的内容在数据段里面发回给客户端。

POST方式:传输的数据不在URL里面出现,而是在数据段里面出现。但是请求头部多了Content-Type和Content-Length两个字段。


请求头部

请求头部由(关键字:<空格>值)对组成,每行一对,关键字和值用英文冒号“:<空格>”分隔。请求头部通知服务器有关于客户端请求的信息,典型的请求头有:

User-Agent:产生请求的浏览器类型。

Accept:客户端可识别的内容类型列表。

Host:请求的主机名,允许多个域名同处一个IP地址,即虚拟主机。

下面是GET包的一个例子:传输的数据在URL里





再看看POST包的例子:传输的数据在数据段里面





HTTP响应报文

HTTP响应也由两个个部分组成,分别是:响应头(状态行+消息报头)+响应正文。

状态行格式如下:

HTTP-Version Status-Code Reason-Phrase CRLF

HTTP-Version表示服务器HTTP协议的版本;Status-Code表示服务器发回的响应状态代码;Reason-Phrase表示状态代码的文本描述。状态代码由三位数字组成,第一个数字定义了响应的类别,且有五种可能取值。

1xx:指示信息–表示请求已接收,继续处理。

2xx:成功–表示请求已被成功接收、理解、接受。

3xx:重定向–要完成请求必须进行更进一步的操作。

4xx:客户端错误–请求有语法错误或请求无法实现。

5xx:服务器端错误–服务器未能实现合法的请求。

下面是http响应包的例子

在前面的一篇文章中,简单了介绍了HTTP报文格式,详情参考 http://www.firefoxbug.net/?cat=47

这里大概介绍下基本的,常见的HTTP包头格式。

POST /report/getComment.jsp HTTP/1.1
Host: yeeg.com
Connection: keep-alive
Content-Length: 161
Origin: http://www.1g1g.com User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7
content-type: application/x-www-form-urlencoded
Accept: */*
Referer: http://www.1g1g.com/player/loader.swf?uid=0.8106261373031884 Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3
Cookie: JSESSIONID=C3F105F72E3602D6292D3E4561E8E400

上面是一个 POST包的包头 ,其中Content-Length字段里面的值就是POST包
数据段的长度 。可以用

wireshark抓取POST包,会发现,post包是把报文头和数据内容分开来发的,会被TCP分片,然后重组。

具体这里不详细讨论。

GET /enclosure/2010-09-10T02_51_05-07_00.mp3 HTTP/1.1
Host: 805665086.podomatic.com
Connection: keep-alive
User-Agent: Mozilla/5.0 (X11; Linux i686) AppleWebKit/535.7 (KHTML, like Gecko) Chrome/16.0.912.77 Safari/535.7
Accept: */*
Referer: http://www.1g1g.com/player/loader.swf?uid=0.8106261373031884 Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.3

上面是一个GET包,GET包请求的资源都是在URL里面的,所以数据段也就没有了,可以通过抓包分析。

HTTP/1.1 200 OK
Date: Tue, 10 Jul 2012 09:12:52 GMT
Server: Apache/2.2.14 (Ubuntu)
Last-Modified: Thu, 23 Dec 2010 19:29:26 GMT
ETag: "960fcf-4a6459-49818e3486374"
Accept-Ranges: bytes
Content-Length: 487
Keep-Alive: timeout=15, max=100
Connection: Keep-Alive
Content-Type: audio/mpeg

上面是一个http响应包,Content-Length指明了数据段的大小。

下面是我今天用C写了解析HTTP报文头的程序。注意:下面代码只主要用libcap实现实现了部分功能,具体

是解析GET包头,POST包头,还有HTTP相应包头,可能存在一些不足,希望大家多多交流。

 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64

65

66

67

68

69

70

71

72

73

74

75

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

117

118

119

120

121

122

123

124

125

126

127

128

129

130

131

132

133

134

135

136

137

138

139

140

141

142

143

144

145

146

147

148

149

150

151

152

153

154

155

156

157

158

159

160

161

162

163

164

165

166

167

168

169

170

171

172

173

174

175

176

177

178

179

180

181

182

183

184

185

186

187

/*

    capture http packet by firefoxbug

*/
#include <pcap.h>
#include <stdio.h>
#include <stdlib.h>                
#include <string.h>
#include <sys/socket.h>
#include <arpa/inet.h>     
#include <net/ethernet.h>
#include <netinet/ip_icmp.h>    //Provides declarations for icmp header

#include <netinet/udp.h>        //Provides declarations for udp header

#include <netinet/tcp.h>        //Provides declarations for tcp header

#include <netinet/ip.h>     //Provides declarations for ip header

#define BUFFSIZE 1500

typedef struct port {

    unsigned int src_port ;

    unsigned int des_port ;
} Port ;

Port port ;
int tcp = 0 ;

FILE * logfile ;
int size ;

void process_packet ( u_char * args
, const struct pcap_pkthdr * header
, const u_char * buffer )
;
char * print_tcp_packet ( const
char * Buffer ) ;
void get_line ( char * data
, char * buff , int length
) ;
void print_http_req_packet ( char * data
) ;
void print_http_ans_packet ( char * data
) ;

int main ( int argc , char
* argv [ ] )
{

    pcap_if_t * alldevsp , * device
;

    pcap_t * handle ; //Handle of the device that shall be sniffed

    char errbuf [ 100 ]
, * devname , devs [ 100
] [ 100 ] ;

    int count = 1 , n ;

    //First get the list of available devices

   
printf ( "Finding available devices ... "
) ;

    if ( pcap_findalldevs ( & alldevsp
, errbuf ) )

    {

       
printf ( "Error finding devices : %s"
, errbuf ) ;

        exit ( 1 ) ;

    }

   
printf ( "Done" ) ;

    //Print the available devices

   
printf ( " \n Available Devices are :
\n " ) ;

    for ( device = alldevsp ; device
!= NULL ; device = device -> next
)

    {

       
printf ( "%d. %s - %s \n "
, count , device -> name , device
-> description ) ;

        if ( device -> name != NULL
)

        {

            strcpy ( devs [ count ]
, device -> name ) ;

        }

        count ++;

    }

    //Ask user which device to sniff

   
printf ( "Enter the number of the device you want to sniff : "
) ;

    scanf ( "%d" , & n
) ;

    devname = devs [ n ] ;

    //Open the device for sniffing

   
printf ( "Opening device %s for sniffing ... "
, devname ) ;

    handle = pcap_open_live ( devname ,
65536 , 1 , 0 , errbuf
) ;

    if ( handle == NULL )

    {

        fprintf ( stderr , "Couldn't open device %s : %s
\n " , devname , errbuf )
;

        exit ( 1 ) ;

    }

   
printf ( "Done \n " )
;

    logfile = fopen ( "log.txt" ,
"w" ) ;

    if ( logfile == NULL )

    {

       
printf ( "Unable to create file."
) ;

    }

    //Put the device in sniff loop

    pcap_loop ( handle , - 1
, process_packet , NULL ) ;

    return 0 ;
}

void process_packet ( u_char * args
, const struct pcap_pkthdr * header
, const u_char * buffer )
{

    size = header -> len ;
//  fprintf(logfile,"length of packet : %d \n",size);  

    //Get the IP Header part of this packet , excluding the ethernet header

    struct iphdr * iph = (
struct iphdr * ) ( buffer +
sizeof ( struct ethhdr )
) ;

    switch ( iph -> protocol )
//Check the Protocol and do accordingly...

    {

        case 6 :   //TCP Protocol

            ++ tcp ;  

        //  printf("TCP : %d \n", tcp);

            unsigned char * data = print_tcp_packet
( buffer ) ;

            if ( size <= 0
)

                break ;

            if ( port. des_port ==
80 )            

            {

                print_http_req_packet ( data ) ;

            }

            else if ( port. src_port
== 80 )

            {

                print_http_ans_packet ( data ) ;

            }

            break ;

    }

}

char * print_tcp_packet ( const
char * Buffer )
{

    //IP header

    struct iphdr * iph = (
struct iphdr * ) ( Buffer   +
sizeof ( struct ethhdr )
) ;

    unsigned int iphdrlen = iph -> ihl
* 4 ;

    //TCP header

    struct tcphdr * tcph = (
struct tcphdr * ) ( Buffer + iphdrlen
+ sizeof ( struct ethhdr
) ) ;

   

    port. src_port = ntohs ( tcph -> source
) ;

    port. des_port = ntohs ( tcph -> dest
) ;

    // mac_header + ip_header + tcp_header

    int header_size =   sizeof (
struct ethhdr ) + iphdrlen + tcph
-> doff * 4 ;

    size = size - header_size ;
//  fprintf(logfile,"length of header : %d \n",header_size );

    return ( char * )
( Buffer + header_size ) ;
}

void print_http_req_packet ( char * data
)
{  

    if ( strncmp ( data ,
"GET" , 3 ) == 0
|| strncmp ( data , "POST"
, 4 ) == 0 )

    {

        fprintf ( logfile , " \n /***********************length of data : %d**********************/
\n " , size ) ;

        fprintf ( logfile , "From %d To %d
\n " , port. src_port , port.
des_port ) ;

        int i = 0 ;  

        for ( ; i < size
; ++ i )

        {

            fprintf ( logfile , "%c"
,* ( data + i ) )
;

        }

        fprintf ( logfile , "/***************************** end *******************************/
\n \n " ) ;

    }

    return ;
}

void print_http_ans_packet ( char * data
)
{

    if ( strncmp ( data ,
"HTTP" , 4 ) ==
0 )

    {

        fprintf ( logfile , " \n /***********************length of data : %d**********************/
\n " , size ) ;

        fprintf ( logfile , "From %d To %d
\n " , port. src_port , port.
des_port ) ;

        char buff [ BUFFSIZE ] =
{ ' \0 ' } ;

        get_line ( data , buff , size
) ;

        fprintf ( logfile , "%s" , buff
) ;

        unsigned int off = strlen ( buff
) ;

        size = size - off ;

        while ( strcmp ( buff ,
" \r \n " ) !=
0 )

        {

            memset ( buff , ' \0 '
, sizeof ( buff ) )
;

            get_line ( data + off , buff
, size ) ;

            fprintf ( logfile , "%s"
, buff ) ;

            off = off + strlen ( buff
) ;

            size = size - off ;

        }

        fprintf ( logfile , "/***************************** end *******************************/
\n \n " ) ;

    }
}

void get_line ( char * data
, char * buff , int length
)
{

    int i = 0 ;

    char ch ;

    for ( ; i < length
;++ i )

    {

        ch = * ( data + i
) ;

        * ( buff + i )
= ch ;

        if ( ch == ' \n '
)

            break ;

    }
}
# gcc parse_http.c -o parse_http -lpcap
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: