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

HTTP请求报文

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

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

HTTP请求头

请求行

请求行由请求方法字段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
#include
#include                
#include
#include
#include      
#include
#include     //Provides declarations for icmp header
#include         //Provides declarations for udp header
#include         //Provides declarations for tcp header
#include     //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


你可能感兴趣的:(http)