实验四 原始套接字编程
一、 实验目的
1.了解Winsock原始套接字编程功能和特点。
2.掌握Winsock原始套接字编程基本方法和步骤。
3.理解ICMP协议在网络中的具体应用及其实现原理。
二、实验内容
1.利用原始套接字编程实现ping命令:
运用原始套接字编程技术,通过ICMP协议所提供的回送请求(echo request)和回送应答(echo reply)报文实现检测目的主机的可达性与状态。
2.(选做)在实现ping命令的基础上,参照教材例6.9设计一个局域网扫描工具。
三、实验原理
Ping的工作原理
ping程序是用来探测主机到主机之间是否可通信,如果不能ping到某台主机,表明不能和这台主机建立连接。ping 使用的是ICMP协议,它发送ICMP回送请求消息给目的主机。ICMP协议规定:目的主机必须返回ICMP回送应答消息给源主机。如果源主机在- -定时间内收到应答,则认为主机可达。
ICMP协议通过IP协议发送的,IP协议是无连接的,不可靠的数据包协议。因此,保证数据送达的工作应该由其他的模块来完成。
使用ping命令可以检测指定设备的在线状态,但在程序中通常不会直接执行ping命令而是通过Socket编程的方式向目标ip地址发送icmp请求包,然后返回等待结果。
四、实验步骤
五、实验小结
附:程序源代码
#include "stdafx.h"
#include <winsock2.h>
#include <ws2tcpip.h>
#include <stdio.h>
#include <stdlib.h>
#pragma comment(lib,"ws2_32.lib")
#define WIN32_LEAN_AND_MEAN
#define IP_RECORD_ROUTE 0x7
#define ICMP_MIN 8
#define ICMP_ECHOREPLY 0
#define ICMP_ECHO 8
#define DEF_PACKET_SIZE 32
#define MAX_PACKET 1024
#define MAX_IP_HDR_SIZE 60
typedef struct iphdr {
unsigned int h_len : 4;
unsigned int version : 4;
unsigned char tos;
unsigned short total_len;
unsigned short ident;
unsigned short frag_and_flags;
unsigned char ttl;
unsigned char proto;
unsigned short checksum;
unsigned int sourceIP;
unsigned int destIP;
}IpHeader;
USHORT checksum(USHORT *buffer, int size) {
unsigned long cksum = 0;
while (size > 1) {
cksum += *buffer++;
size -= sizeof(USHORT);
}
if (size) {
cksum += *(UCHAR*)buffer;
}
cksum = (cksum >> 16) + (cksum & 0xffff);
cksum += (cksum >> 16);
return (USHORT)(~cksum);
}
typedef struct _ihdr {
BYTE i_type;
BYTE i_code;
USHORT i_cksum;
USHORT i_id;
USHORT i_seq;
ULONG timestamp;
}IcmpHeader;
void fill_icmp_data(char * icmp_data, int datasize){
IcmpHeader *icmp_hdr;
char *datapart;
icmp_hdr = (IcmpHeader*)icmp_data;
icmp_hdr->i_type = ICMP_ECHO;
icmp_hdr->i_code = 0;
icmp_hdr->i_id = (USHORT)GetCurrentThreadId();
icmp_hdr->i_cksum = 0;
icmp_hdr->i_seq = 0;
datapart = icmp_data + sizeof(IcmpHeader);
memset(datapart, 'E', datasize - sizeof(IcmpHeader));
}
int decode_icmp_resp(char *buf, int bytes, sockaddr_in *from, DWORD tid)
{
IpHeader *iphdr;
IcmpHeader *icmphdr;
unsigned short iphdrlen;
iphdr = (IpHeader*)buf;
iphdrlen = iphdr->h_len * 4;
if (bytes < iphdrlen + ICMP_MIN)
{
return -1;
}
icmphdr = (IcmpHeader*)(buf + iphdrlen);
if (icmphdr->i_type != ICMP_ECHOREPLY)
{
return -2;
}
if (icmphdr->i_id != (USHORT)tid)
{
return -3;
}
int time = GetTickCount() - (icmphdr->timestamp);
if (time >= 0)
{
return time;
}
else
{
return -4;
}
}
int ping(const char *ip, DWORD timeout)
{
WSADATA wsa;
SOCKET sockRaw = NULL;
sockaddr_in dest, from;
hostent *hp;
int datasize;
char *dest_ip;
char *icmp_data = NULL;
char *recvbuf = NULL;
USHORT seq_no = 0;
int retval;
int ret;
if (WSAStartup(MAKEWORD(2, 1), &wsa) != 0){
ret = -1000;
goto FIN;
}
sockRaw = WSASocket(AF_INET,
SOCK_RAW,
IPPROTO_ICMP,
NULL, 0, WSA_FLAG_OVERLAPPED);
if (sockRaw == INVALID_SOCKET) {
ret = -2;
goto FIN;
}
int bread = setsockopt(sockRaw, SOL_SOCKET, SO_RCVTIMEO, (char*)&timeout,
sizeof(timeout));
if (bread == SOCKET_ERROR) {
ret = -3;
goto FIN;
}
bread = setsockopt(sockRaw, SOL_SOCKET, SO_SNDTIMEO, (char*)&timeout,
sizeof(timeout));
if (bread == SOCKET_ERROR) {
ret = -4;
goto FIN;
}
memset(&dest, 0, sizeof(dest));
unsigned int addr = 0;
hp = gethostbyname(ip);
if (!hp){
addr = inet_addr(ip);
}
if ((!hp) && (addr == INADDR_NONE)) {
ret = -5;
goto FIN;
}
if (hp != NULL)
memcpy(&(dest.sin_addr), hp->h_addr, hp->h_length);
else
dest.sin_addr.s_addr = addr;
if (hp)
dest.sin_family = hp->h_addrtype;
else
dest.sin_family = AF_INET;
dest_ip = inet_ntoa(dest.sin_addr);
datasize = DEF_PACKET_SIZE;
datasize = datasize + sizeof(IcmpHeader);
char icmp_dataStack[MAX_PACKET];
char recvbufStack[MAX_PACKET];
icmp_data = icmp_dataStack;
recvbuf = recvbufStack;
if (!icmp_data)
{
ret = -6;
goto FIN;
}
memset(icmp_data, 0, MAX_PACKET);
fill_icmp_data(icmp_data, datasize);
DWORD startTime = GetTickCount();
((IcmpHeader*)icmp_data)->timestamp = startTime;
((IcmpHeader*)icmp_data)->i_cksum = checksum((USHORT*)icmp_data, datasize);
int bwrote;
bwrote = sendto(sockRaw, icmp_data, datasize, 0, (struct sockaddr*)&dest, sizeof(dest));
if (bwrote == SOCKET_ERROR)
{
if (WSAGetLastError() != WSAETIMEDOUT)
{
ret = -7;
goto FIN;
}
}
if (bwrote < datasize)
{
ret = -8;
goto FIN;
}
LARGE_INTEGER ticksPerSecond;
LARGE_INTEGER start_tick;
LARGE_INTEGER end_tick;
double elapsed;
QueryPerformanceFrequency(&ticksPerSecond);
QueryPerformanceCounter(&start_tick);
int fromlen = sizeof(from);
while (1)
{
retval = recvfrom(sockRaw, recvbuf, MAX_PACKET, 0, (struct sockaddr*)&from, &fromlen);
if (retval == SOCKET_ERROR)
{
if (WSAGetLastError() == WSAETIMEDOUT)
{
ret = -1;
goto FIN;
}
ret = -9;
goto FIN;
}
int time = decode_icmp_resp(recvbuf, retval, &from, GetCurrentThreadId());
if (time >= 0)
{
QueryPerformanceCounter(&end_tick);
elapsed = ((double)(end_tick.QuadPart - start_tick.QuadPart) / ticksPerSecond.QuadPart);
ret = (int)(elapsed * 1000);
goto FIN;
}
else if (GetTickCount() - startTime >= timeout || GetTickCount() < startTime)
{
ret = -1;
goto FIN;
}
}
FIN:
closesocket(sockRaw);
WSACleanup();
return ret;
}
int main(int argc, char **argv) {
printf("ping %s\n", argv[1]);
int ret = ping(argv[1], 5000);
if (ret >= 0)
{
printf("%s在线,用时%dms\n", argv[1], ret);
}
else
{
printf("%d\n", ret);
}
system("pause");
return 0;
}