//-----------------------iphdr.h-----------------------//
//源码分析将忽略ipv6
//边界
对齐至字节
pshpack1.h为官方头文件,不做赘述。
#include
<pshpack1.h>
// 1 -- ipv4
头部
typedef
struct ip_hdr
{
unsigned char ip_verlen; //
前4位
IP
版本号
(IPv4
或者
IPv6)
//
后4位头部长度(32位,4字节)(1.1)
unsigned char ip_tos; //
前3位为优先级,后5位为服务类型(1.2)
unsigned short ip_totallength; // 16
位包总长度包括头部和数据(字节)(1.3)
unsigned short ip_id; // 16
位
ID
标识
unsigned short ip_offset; //
前3位为分段标识,后5位为分段偏移
unsigned char ip_ttl; //
该包可经过的路由器数量上限
unsigned char ip_protocol; //
协议类型(
TCP
,
UDP
,
ICMP
,
IGMP
等)
unsigned short ip_checksum; //
ipv4 头部的校验和
unsigned int ip_srcaddr; // ipv4
源地址
unsigned int ip_destaddr; // ipv4
目的地址
} IPV4_HDR, *PIPV4_HDR, FAR * LPIPV4_HDR;
此为IPv4头部定义。IPv4头部一般为20字节大小除非使用到选项位。(1.1) 由于最大头部的限制,ping所具有路由记录功能在如今的网络拓扑中无法使用(只能记录9个ipv4地址)。此功能由traceroute替代。
(1.2) 以上参照CCNA Study Guide的说法。在TCP/IP illustrated Volume I中前3位被忽略,后4位分别代表minimize delay, maximize throughput, maximize reliability, 和minimize monetray cost,最后1位被置0并忽略。
(1.3) 理论上ipv4可以达到的最大长度为65536字节,除了在超级通道(hyperchannel)中出现这种最大传输单元外,在普通网络中通常只允许 8192字节大小的包传输。TCP为流协议没有大小限制,UDP最大包为512字节。一台主机一次可接收的最大数据包为576字节。
// 2 -- ipv4 选项头部
typedef
struct ipv4_option_hdr
{
unsigned char opt_code;
// ipv4 选项头类型
unsigned char opt_len;
// ipv4 选项头长度
unsigned char opt_ptr;
// ipv4 选项头指针
unsigned long opt_addr[9];
// ipv4 9个地址列表(2.1)
} IPV4_OPTION_HDR, *PIPV4_OPTION_HDR, FAR *LPIPV4_OPTION_HDR;
(2.1) 参照(1.1)
// 3 -- icmp 头部
typedef
struct icmp_hdr
{
unsigned char icmp_type;
// icmp 类型
unsigned char icmp_code;
// ipv4 码
unsigned short icmp_checksum;
// icmp 头部及数据校验和
unsigned short icmp_id;
// icmp id标识(3.1)
unsigned short icmp_sequence;
// icmp 序列号,请求回应消息对
} ICMP_HDR, *PICMP_HDR, FAR *LPICMP_HDR;
(3.1) id标识一般为发送icmp回显请求的进程号
// 4 -- udp 头部(此头部未在程序中用到)
typedef
struct udp_hdr
{
unsigned short src_portno;
// 源端口
unsigned short dst_portno;
// 目的端口
unsigned short udp_length;
// udp 包总长度(字节)
unsigned short udp_checksum;
// udp 头部以及数据校验和(4.1)
} UDP_HDR, *PUDP_HDR;
(4.1) UDP,TCP校验和都包括头部和数据。IP校验和只涉及头部。UDP校验和可选,TCP为强制。
// 5 -- ipv4 路径记录宏
#define
IP_RECORD_ROUTE 0x7
// icmp 类型和码(5.1)
#define
ICMPV4_ECHO_REQUEST_TYPE 8
// icmp 回显请求类型
#define
ICMPV4_ECHO_REQUEST_CODE 0
// icmp 回显请求码
#define
ICMPV4_ECHO_REPLY_TYPE 0
// icmp 回显回应类型
#define
ICMPV4_ECHO_REPLY_CODE 0
// icmp 回显回应码
#define
ICMPV4_MINIMUM_HEADER 8
// icmp 最小头部
(5.1) 参照TCP/IP Illustrated : Volume 1 Chapter 6 ICMP ICMP 消息类型
//-----------------------resolve.h---------------------//
// 恢复默认对齐方式
#include
<poppack.h>
#ifndef _RESOLVE_H_
#define
_RESOLVE_H_
// 在C++编译器中以C语言的方式编译
#ifdef
_cplusplus
extern "C" {
#endif
int
PrintAddress(SOCKADDR *sa, int salen);
int
FormatAddress(SOCKADDR *sa, int salen, char *addrbuf, int addrbuflen);
int
ReverseLookup(SOCKADDR *sa, int salen, char *namebuf, int namebuflen);
struct
addrinfo *ResolveAddress(char *addr, char *port, int af, int type, int proto);
#ifdef
_cplusplus
}
#endif
#endif
//-----------------------resolve.cpp---------------------//
// 6
#include
<winsock2.h>
// socket 标准头文件
#include
<ws2tcpip.h>
// TCP/IP实现相关(6.1)
#include
<strsafe.h>
// 提供安全的字符串操作(6.2)
#include
<stdio.h>
#include
<stdlib.h>
#include
"resolve.h"
(6.1) 此头文件提供 getnameinfo,getaddrinfo函数。
(6.2) StringCchPrintf, StringCchCopy 具有相对于printf 和strcpy函数更多的缓冲区安全机制。
(6.2) StringCchPrintf, StringCchCopy 具有相对于printf 和strcpy函数更多的缓冲区安全机制。
// 7
int
PrintAddress(SOCKADDR *sa, int salen)
{
char host[NI_MAXHOST],
serv[NI_MAXSERV];
int hostlen = NI_MAXHOST,
servlen = NI_MAXSERV,
rc;
rc = getnameinfo(
// 提供协议无关的名字解析(7.1)
sa,
salen,
host,
hostlen,
serv,
servlen,
NI_NUMERICHOST | NI_NUMERICSERV
);
if (rc != 0)
{
fprintf(stderr, "%s: getnameinfo failed: %d/n", __FILE__, rc);
return rc;
}
if (strcmp(serv, "0") != 0)
{
if (sa->sa_family == AF_INET)
printf("[%s]:%s", host, serv);
else
printf("%s:%s", host, serv);
}
else
printf("%s", host);
return NO_ERROR;
}
(7.1) host 缺省返回为一个在网络上的完整域名。serv返回为一个端口服务名。NI_NUMBERICHOST | NI_NUMBERICSERV 意味着返回以数字形式表示的host(IP地址)和serv(端口号)。
此函数与ResolveAddress函数的区别为此函数将addrinfo结构中sockaddr翻译成为可读信息。
此函数与ResolveAddress函数的区别为此函数将addrinfo结构中sockaddr翻译成为可读信息。
int
FormatAddress(SOCKADDR *sa, int salen, char *addrbuf, int addrbuflen)
{
char host[NI_MAXHOST],
serv[NI_MAXSERV];
int hostlen = NI_MAXHOST,
servlen = NI_MAXSERV,
rc;
HRESULT hRet;
rc = getnameinfo(
sa,
salen,
host,
hostlen,
serv,
servlen,
NI_NUMERICHOST | NI_NUMERICSERV
);