1. 程式人生 > >自己動手學TCP/IP–http協議(http報文頭)

自己動手學TCP/IP–http協議(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相應包頭,可能存在一些不足,希望大家多多交流。

/*
	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("\nAvailable 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;
	}
}