0xAA55 发表于 2014-3-11 18:42:52

【TCPIP】借助Winsock编写HTTP转发程序(虽然没啥用但是有代码)

我来说一下浏览器是怎么上网的。首先,网站的服务器程序开启之后,它会通过TCPIP协议监听80端口并等待连接。
然后,用户通过浏览器上网,浏览器会尝试连接到服务器,连接完成以后,浏览器按照HTTP协议发送一个数据包(HTTP请求)。
服务器接收到浏览器的数据包以后,回复一个数据包给浏览器。这个数据包包括了一个HTTP头和一些其余的数据(比如网页文件、图片等)。
浏览器接收到服务器的数据包以后,显示出网页的内容。这就是浏览器的运行方式。

虽然HTTP协议我们可以自己从网上找到学习的资料,但是如果我们能自己截获这些数据包,我们就能更容易地学到它的原理。因此我写了一个转发程序。
程序运行的原理:通过TCP/IP协议监听80端口。当有浏览器连接到它的时候,它就去连接一个别的、我们指定了的网页(比如www.baidu.com)
然后把浏览器的数据包转发到网站服务器(百度)(顺带再把数据包发一份到stdout)。然后百度的服务器就会回复一个数据包,我这个转发器就截获百度的数据包并转发给浏览器(顺带再把数据包发一份到stdout)。我没有研究过HTTP协议,因此我这里就不给出HTTP协议的详细的细节了。#include<stdio.h>
#include<process.h>
#include<winsock2.h>

#define BUFSIZE 8192    //缓冲区大小

#define TARGET_WEBSITE"www.baidu.com"//要访问的网站

SOCKET g_SockToServer=INVALID_SOCKET;
SOCKET g_SockToBrowser=INVALID_SOCKET;

void Repeater1(void *p)//转发器1:接收浏览器数据,转发到服务器
{
    char buffer={0};
    int moredata=0;
    int nRecv=0;
    for(;;)//从浏览器接收数据
    {
      memset(buffer,0,sizeof(buffer));
      nRecv=recv(g_SockToBrowser,buffer,BUFSIZE,0);//接收数据
      if(WSAGetLastError()==WSAEMSGSIZE)//如果WSAGetLastError为WSAEMSGSIZE,表示数据包大小超过了缓冲区
      {
            moredata=1;
            nRecv=BUFSIZE;
      }
      else
            moredata=0;
      printf("浏览器数据包:\n%s\n",buffer);//输出到stdout
      if(send(g_SockToServer,buffer,nRecv,0)!=nRecv)//转发
      {
            puts("转发失败。");
            break;
      }
      if(!moredata)
            break;
    }
}

void Repeater2(void *p)//转发器2:接收服务器数据,转发到浏览器
{
    char buffer={0};
    int moredata=0;
    int nRecv=0;
    for(;;)//从服务器接收数据
    {
      memset(buffer,0,sizeof(buffer));
      nRecv=recv(g_SockToServer,buffer,BUFSIZE,0);//接收数据
      if(WSAGetLastError()==WSAEMSGSIZE)
      {
            moredata=1;
            nRecv=BUFSIZE;
      }
      else
            moredata=0;
      printf("服务器数据包:\n%s\n",buffer);//输出到stdout
      if(send(g_SockToBrowser,buffer,nRecv,0)!=nRecv)
      {
            puts("转发失败。");
            break;
      }
      if(!moredata)
            break;
    }
}

void DealWithConn(SOCKET s,void*pBuffer,int nRecv)//处理单个连接的程序
{
    g_SockToBrowser=s;
    g_SockToServer=socket(AF_INET,SOCK_STREAM,0);
    do
    {
      SOCKADDR_IN sAddr={0};//连接目标
      struct hostent *phostaddr;//域名解析
      if(g_SockToServer==INVALID_SOCKET)
      {
            puts("socket失败\n");
            break;
      }
      phostaddr=gethostbyname(TARGET_WEBSITE);//解析域名
      if(!phostaddr)
      {
            puts("域名解析失败。");
            break;
      }
      sAddr.sin_family=AF_INET;
      sAddr.sin_port=htons(80);
      sAddr.sin_addr.s_addr=*(u_long*)(phostaddr->h_addr_list);
      printf("服务器IP:%u.%u.%u.%u\n",
            (unsigned char)phostaddr->h_addr_list,
            (unsigned char)phostaddr->h_addr_list,
            (unsigned char)phostaddr->h_addr_list,
            (unsigned char)phostaddr->h_addr_list);//打印IP
      if(connect(g_SockToServer,(SOCKADDR*)&sAddr,sizeof(sAddr)))//连接到服务器
      {
            puts("连接不上目标。");
            break;
      }
      _beginthread(Repeater1,0,NULL);//两个转发器同步执行
      Repeater2(NULL);
      puts("已断开连接。");
      closesocket(g_SockToServer);//关闭连接
      closesocket(g_SockToBrowser);
      return;
    }while(0);
    printf("出BUG了。\nWSAGetLastError=%u\n",WSAGetLastError());
    if(g_SockToServer!=INVALID_SOCKET)
      closesocket(g_SockToServer);
    closesocket(g_SockToBrowser);
}

int main()
{
    WSADATA wsaData;
    SOCKET sockListener=INVALID_SOCKET;
    SOCKET sockConn=INVALID_SOCKET;
    if(WSAStartup(WINSOCK_VERSION,&wsaData))//初始化Winsock库
    {
      printf("WSAStartup失败。\n");
      return 1;
    }
    do
    {
      SOCKADDR_IN sAddr={0};
      char cDataRecv={0};//多分配一个字节
      int nRecv=0;
      sockListener=socket(AF_INET,SOCK_STREAM,0);
      if(sockListener==INVALID_SOCKET)
      {
            printf("socket失败\n");
            break;
      }
      sAddr.sin_family=AF_INET;
      sAddr.sin_port=htons(80);
      sAddr.sin_addr.s_addr=INADDR_ANY;
      if(bind(sockListener,(SOCKADDR*)&sAddr,sizeof(sAddr)))//绑定端口
      {
            printf("bind失败\n");
            break;
      }
      if(listen(sockListener,SOMAXCONN))//监听
      {
            printf("listen失败\n");
            break;
      }
      for(;;)
      {
            puts("等待建立连接。");
            sockConn=accept(sockListener,NULL,NULL);//等待建立连接
            if(sockConn!=INVALID_SOCKET)
            {
                puts("已连接。");
                DealWithConn(sockConn,cDataRecv,nRecv);//处理单个连接
            }
      }
      closesocket(sockListener);
      return 0;
    }while(0);
    printf("遇到了某些问题。\nWSAGetLastError=%u\n",WSAGetLastError());
    if(sockListener!=INVALID_SOCKET)
      closesocket(sockListener);
    if(sockConn!=INVALID_SOCKET)
      closesocket(sockConn);
    return 1;
}详细的使用方法:
方法1、运行它,然后用浏览器直接上“http://localhost/”或“http://127.0.0.1/”虽然看不到数据,但是你能捕获浏览器给出的HTTP头。
方法2、运行它之前改hosts,把目标网站域名重定向到127.0.0.1,然后运行这个程序,用浏览器上目标网站,能捕获到完整的数据。
方法3、运行它,然后在局域网的另一台电脑(或虚拟机)用浏览器访问你的电脑IP。可以看到浏览器发送的数据包。

提示:
1、开启了IIS服务或Apache服务的请事先关闭这两个服务,以免因端口冲突导致这个程序无法运行。
2、我不能保证这个程序的可移植性(因为用到了WSAGetLastError来判断缓冲区外是否还有数据)。
3、本程序由0xAA55原创。转载请指明出处。
4、完整的VC6工程以及编译生成的EXE下载:(请先回帖。回帖后就能下载。)**** Hidden Message *****
补充一下,我这里给出两个截获的HTTP头的范例。一定要注意HTTP头后面的两个空行!
以下内容是IE10发送给服务器的数据包:GET /网页文件路径 HTTP/1.1
Accept: text/html, application/xhtml+xml, */*
Accept-Language: zh-CN
User-Agent: Mozilla/5.0 (Windows NT 6.1; Trident/7.0; rv:11.0) like Gecko
Accept-Encoding: gzip, deflate
Host: 网站域名
DNT: 1
Connection: Keep-Alive
 
 以下内容是谷歌浏览器(Chrome)发送给服务器的数据包:GET /网页文件路径 HTTP/1.1
Host: 网站域名
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
User-Agent: Mozilla/5.0 (Windows NT 6.1) AppleWebKit/537.36 (KHTML, like Gecko)Chrome/33.0.1750.146 Safari/537.36
Accept-Encoding: gzip,deflate,sdch
Accept-Language: zh-CN,zh;q=0.8,en;q=0.6,ja;q=0.4
 
 

系统 发表于 2014-3-11 19:56:43

系统自动沙发

元始天尊 发表于 2014-3-11 21:41:51

原来监听器是这么写的

技界 发表于 2014-3-24 22:42:04

哈哈,正看这方面的代码的了~

我有个梦 发表于 2014-3-26 16:26:00

学习了。good

langzhe 发表于 2017-11-22 17:18:03

666666666666666666

卡卡 发表于 2017-11-23 14:15:57

秋风扫落叶 发表于 2017-12-20 23:54:00

转发程序现在挺多的
页: [1]
查看完整版本: 【TCPIP】借助Winsock编写HTTP转发程序(虽然没啥用但是有代码)