原始socket例子

ICMP实现例子

// ICMP.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include <WinSock2.h>
#include <tchar.h>
#include <WS2tcpip.h>
#include <windows.h>
#pragma comment(lib, "Ws2_32.lib")

#define ECHO_REQUER  8  //请求
#define ECHO_REPLY   0  //回复


struct IcmpHeader
{
  unsigned char  icmp_type;     //类型
  unsigned char  icmp_code;     //代码
  unsigned short icmp_cksum;  //效验和
  unsigned short icmp_id;     //标识
  unsigned short icmp_seq;    //序列号
  unsigned int   icmp_data;   //GetTickout()
};

struct IpHeader
{
  unsigned char h_len : 4;       //头部长度
  unsigned char version : 4;     //ip协议的版本好
  unsigned char tos;             //区分服务
  unsigned short total_len;      //包的总长度(首和数据之和)
  unsigned short ident;          //唯一标识符
  unsigned short frag_and_flags; //标志位
  unsigned char ttl;             //ttl(包在网络传输的生存时间)
  unsigned char proto;           //数据包的协议(TCP ,UDP etc)
  unsigned short checksum;       //IP首部校验和 checksum
  unsigned int sourceIP;         //发送方ip地址
  unsigned int destIP;           //接收方ip地址
};



void reportError(const char* pre)
{
  //#if win_32
  //#if linux
  LPVOID lpMsgBuf;
  FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER |
    FORMAT_MESSAGE_FROM_SYSTEM |
    FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    GetLastError(),
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    (LPTSTR)&lpMsgBuf,
    0,
    NULL
    );

  printf("%s:%s", pre, lpMsgBuf);


  LocalFree(lpMsgBuf);

}

void showMsg(const char* msg)
{
  printf("%s\n", msg);
}

//网际校验和是被校验数据16位值的反码和(ones-complement sum)
WORD CalcCheckSum(IN unsigned short* addr, IN int len)
{
  int		nleft = len;
  int		sum = 0;
  unsigned short* w = addr;
  unsigned short answer = 0;

  while (nleft > 1) {
    sum += *w++;
    nleft -= 2;
  }

  if (nleft == 1) {
    *(unsigned char*)(&answer) = *(unsigned char*)w;
    sum += answer;
  }

  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  answer = ~sum;

  return (answer);
}

int _tmain(int argc, _TCHAR* argv[])
{
  //只有windows初始化socket
  WSADATA wsaData;
  WSAStartup(MAKEWORD(2, 2), &wsaData);
  //ICMP用的是原始套接字类型
  int nSocket = socket(AF_INET, SOCK_RAW, IPPROTO_ICMP);
  showMsg("socket ok");
  
  //根据域名获取ip地址
  char szDomain[20] = { 0 };
  printf("请输入你要ping的域名:");
  scanf_s("%s", szDomain, 20);
  sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = 0;
  struct hostent *host = gethostbyname(szDomain);
  if (host == NULL)
  {
    puts("无效域名");
    return 0;
  }
  addr.sin_addr.S_un.S_addr = *(int*)(host->h_addr_list[0]);
  
  //ICMP协议头
  char szIcmpHead[32] = { 0 };
  IcmpHeader* pIcmpHeader = (IcmpHeader*)szIcmpHead;
  pIcmpHeader->icmp_type = ECHO_REQUER;
  pIcmpHeader->icmp_code = 0;
  pIcmpHeader->icmp_cksum = 0;
  pIcmpHeader->icmp_id = 1;
  pIcmpHeader->icmp_seq = 0;
  pIcmpHeader->icmp_data = ::GetTickCount();
  //校验和0先给0 最后才计算,不然的话校验和的值算不出来
  //先把其他的赋值后就知道怎么计算校验和的,因为校验和也是成员,先赋值的话,校验和值就会算错
  pIcmpHeader->icmp_cksum = CalcCheckSum((unsigned short*)szIcmpHead, sizeof(szIcmpHead));
  
  //发送ICMP协议头
  int nBytes = sendto(nSocket, szIcmpHead, sizeof(szIcmpHead), 0, (sockaddr*)&addr, sizeof(addr));
  if (nBytes < 0)
  {
    reportError("sendto");
  }
  sockaddr_in stAddr;
  char szRecvBuf[200] = { 0 };
  int nLen = sizeof(stAddr);
  //设置超时时间,没有收到包就放弃[IP][ICMP]
  timeval stTv = { 3, 0 };//秒,微秒
  fd_set stFd;//socket数组
  FD_ZERO(&stFd);//初始化
  FD_SET(nSocket, &stFd);//把socket添加进数组
  int nRet = select(0, &stFd, NULL, NULL, &stTv);
  if (nRet == 0)
  {
    reportError("select");
    puts("超时");
    system("pause");
    return 0;
  }

  else if (nRet != SOCKET_ERROR)
  {
    nBytes = recvfrom(nSocket, szRecvBuf, sizeof(szRecvBuf), 0, (sockaddr*)&stAddr, &nLen);
    if (nBytes > 0)
    {
      IpHeader* pIpHead = (IpHeader*)szRecvBuf;
      //IP层前面是IP头,所以拆包的时候获取到ICMP的头,把指针以一个IP头的步长
      IcmpHeader* pIcmpHeader = (IcmpHeader*)(pIpHead + 1);
      printf("来自 %s 的回复: 字节=32 时间=%dms TTL=%d\n",
        inet_ntoa(*(in_addr*)&pIpHead->sourceIP),
        ::GetTickCount() - pIcmpHeader->icmp_data,
        pIpHead->ttl);
    }
  }
  system("pause");
	return 0;
}

原始socket例子

#include <WinSock2.h>
#include <MSTcpIP.h>
#include <tchar.h>
#include <WS2tcpip.h>
#include <windows.h>
#include <stdio.h>
#pragma comment(lib, "Ws2_32.lib")

struct IcmpHeader
{
  unsigned char  icmp_type;     //类型
  unsigned char  icmp_code;     //代码
  unsigned short icmp_cksum;  //效验和
  unsigned short icmp_id;     //标识
  unsigned short icmp_seq;    //序列号
  unsigned int   icmp_data;   //GetTickout()
};

struct IpHeader
{
  unsigned char h_len : 4;       //头部长度
  unsigned char version : 4;     //ip协议的版本好
  unsigned char tos;             //区分服务
  unsigned short total_len;      //包的总长度(首和数据之和)
  unsigned short ident;          //唯一标识符
  unsigned short frag_and_flags; //标志位
  unsigned char ttl;             //ttl(包在网络传输的生存时间)
  unsigned char proto;           //数据包的协议(TCP ,UDP etc)
  unsigned short checksum;       //IP首部校验和 checksum
  unsigned int sourceIP;         //发送方ip地址
  unsigned int destIP;           //接收方ip地址
};

struct TcpHeader
{
  unsigned short nSourPort;   // 源端口号16bit
  unsigned short nDestPort;   // 目的端口号16bit
  unsigned int   nSequNum;   // 序列号32bit
  unsigned int   nAcknowledgeNum; // 确认号32bit
  unsigned short nHLenAndFlag;  // 前4位:TCP头长度;中6位:保留;后6位:标志位16bit
  unsigned short nWindowSize;  // 窗口大小16bit
  unsigned short nCheckSum;   // 检验和16bit
  unsigned short nrgentPointer;  // 紧急数据偏移量16bit
};


struct UdpHeader
{
  unsigned short nSourPort;   // 源端口号16bit
  unsigned short nDestPort;   // 目的端口号16bit
  unsigned short nLength;   // 数据包长度16bit
  unsigned short nCheckSum;   // 校验和16bit
};

void reportError(const char* pre)
{
  LPVOID lpMsgBuf;
  FormatMessage(
    FORMAT_MESSAGE_ALLOCATE_BUFFER |
    FORMAT_MESSAGE_FROM_SYSTEM |
    FORMAT_MESSAGE_IGNORE_INSERTS,
    NULL,
    GetLastError(),
    MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
    (LPTSTR)&lpMsgBuf,
    0,
    NULL
    );

  printf("%s:%s", pre, lpMsgBuf);


  LocalFree(lpMsgBuf);

}


void showMsg(const char* msg)
{
  printf("%s\n", msg);
}

//网际校验和是被校验数据16位值的反码和(ones-complement sum)
WORD CalcCheckSum(IN unsigned short* addr, IN int len)
{
  int		nleft = len;
  int		sum = 0;
  unsigned short* w = addr;
  unsigned short answer = 0;

  while (nleft > 1) {
    sum += *w++;
    nleft -= 2;
  }

  if (nleft == 1) {
    *(unsigned char*)(&answer) = *(unsigned char*)w;
    sum += answer;
  }

  sum = (sum >> 16) + (sum & 0xffff);
  sum += (sum >> 16);
  answer = ~sum;

  return (answer);
}

int main()
{
  WSADATA wsaData;
  WSAStartup(MAKEWORD(2, 2), &wsaData);

  int nSocket = socket(AF_INET, SOCK_RAW, IPPROTO_IP);
  if (nSocket <= 0) 
  {
    reportError("socket");
    return 0;
  }
  showMsg("socket ok");

  sockaddr_in addr;
  addr.sin_family = AF_INET;
  addr.sin_port = 0;
  addr.sin_addr.S_un.S_addr =htonl(INADDR_LOOPBACK);

  if (bind(nSocket, (sockaddr*)&addr, sizeof(addr)) < 0)
  {
    reportError("bind");
    return 0;
  }
  //设置网卡为混杂模式,也叫泛听模式。可以侦听经过的所有的包(包括接收和发送的数据包 )
  int optval = 1;
  DWORD dwBytesRet;
  int nRet = WSAIoctl(nSocket, SIO_RCVALL, &optval, sizeof(optval), nullptr, 0, &dwBytesRet, nullptr, nullptr);
  if (nRet == SOCKET_ERROR)
  {
    reportError("WSAIoctl");
    return 0;
  }

  /*[IP][TCP/UDP]*/
  char szRecvBuff[0x1000] = { 0 };
  while (true)
  {
    nRet = recv(nSocket, szRecvBuff, sizeof(szRecvBuff), 0);
    if (nRet > 0)
    {
      //拿到IP协议头输出目标和源ip
      IpHeader* pIphead = (IpHeader*)szRecvBuff;
      printf("%-15s ==> %-15s bytes:%d",
        inet_ntoa(*(in_addr*)&pIphead->sourceIP),
        inet_ntoa(*(in_addr*)&pIphead->destIP),
        nRet);
      //通过IP协议头判断数据包是那种协议
      switch (pIphead->proto)
      {
      case IPPROTO_TCP:
      {  
        TcpHeader* pTcphead = (TcpHeader*)(pIphead + 1);
        printf(" IPPROTO_TCP sourcePort:%d destPort:%d\n",
          htons(pTcphead->nSourPort), htons(pTcphead->nDestPort));
        break;
      }
       
      case IPPROTO_UDP:
      {
        //同上可以拆包得到UDP的头获取UDP的数据信息
        UdpHeader* pUcphead = (UdpHeader*)(pIphead + 1);
        printf(" IPPROTO_UDP sourcePort:%d destPort:%d\n",
          htons(pUcphead->nSourPort), htons(pUcphead->nDestPort));
        break;
      }
      case IPPROTO_ICMP:
        printf(" IPPROTO_ICMP\n");
      }
    }
  }


  system("pause");
  return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值