WINDOWS网络编程技术现.docx
- 文档编号:28965814
- 上传时间:2023-07-20
- 格式:DOCX
- 页数:35
- 大小:623KB
WINDOWS网络编程技术现.docx
《WINDOWS网络编程技术现.docx》由会员分享,可在线阅读,更多相关《WINDOWS网络编程技术现.docx(35页珍藏版)》请在冰豆网上搜索。
WINDOWS网络编程技术现
下载
第13章原始套接字
利用“原始套接字”(RawSocket),我们可访问位于基层的传输协议。
本章专门讲解如
何运用这种原始套接字,来模拟IP的一些实用工具,比如Traceroute和Ping程序等等。
使用原
始套接字,亦可对IP头信息进行实际的操作。
本章只关心IP协议;至于如何针对其他协议使
用原始套接字,我们不打算提及。
而且,大多数协议(除ATM以外)根本就不支持原始套接
字。
所有原始套接字都是使用SOCK_RAW这个套接字类型来创建的,而且目前只有Winsock
2提供了对它的支持。
因此,无论MicrosoftWindowsCE还是老版本的Windows95(无
Winsock2升级)均不能利用原始套接字的能力。
此外,要想顺利使用原始套接字,要求对基层的协议结构有一定程度的认识,而那已超
出了本书的范围。
在这一章中,我们打算讨论Internet控制消息协议(ICMP)、Internet组管理
协议(IGMP)以及用户数据报协议(UDP)。
ICMP会由Ping这个实用程序用到,以便探测到
某个主机的路由是否有效和畅通,看看对方的机器是否会作出响应。
对程序开发者来说,经
常都要用到一种程序化的方法,以便判断一台机器是否“活动”,网络数据能否抵达它。
IP多
播通信利用IGMP将多播组成员信息通告给路由器。
大多数Win32平台目前都增加了对IGMP
第2版的支持。
但在某些情况下,我们也需要送出自己的IGMP数据包,以便脱离组成员关系。
至于UDP协议,我们打算把它同IP_HDRINCL这个套接字选项组合起来讨论。
以它为例,讲
述如何发送自己的IGMP包。
对这三种协议来说,我们都只会讲解与本章示范代码及示范程序
密切相关的那些部分。
13.1原始套接字的创建
要想使用原始套接字,第一步便是创建它。
可用socket命令或WSASocket调用来做到这一
点。
注意在典型情况下,在Winsock为IP列出的目录中,并不存在SOCK_RAW这一套接字类
型。
然而,这并不能妨碍我们创建此种类型的套接字。
它的意思只是说,我们不能用一个
WSAPROTOCOL_INFO结构来创建一个原始套接字。
请参考第5章,那里详细讲述了如何用
WSAEnumProtocols函数以及WSAPROTOCOL_INFO结构来列举协议条目。
注意在套接字的
创建过程中,必须自行设定SOCK_RAW标志。
下述代码片断解释了如何将ICMP作为一种基
层IP协议,来完成一个原始套接字的创建:
由于原始套接字使人们能对基层传输机制加以控制,所以有些人将其用于不法用途,从
下载
第13章计原始套接字计
计341
而造成了WindowsNT下一个潜在的安全漏洞。
因此,只有属于“管理员”(Administrators)
组的成员,才有权创建类型为SOCK_RAW的套接字。
而Windows95和Windows98均未施加
这方面的限制。
要想在WindowsNT中绕过这一限制,可考虑禁止对原始套接字的安全检查。
方法是在注
册表创建如下变量,并将它的值设为1(DWORD类型):
更改了注册表后,注意重新启动计算机。
在上述示范代码中,我们采用的是ICMP协议。
但假如想使用IGMP、UDP、IP或者原始
IP,只需分别设置IPPROTO_IGMP、IPPROTO_UDP、IPPROTO_IP或者IPPROTO_RAW即可。
然而,请注意其中存在的一处限制:
在WindowsNT4、Windows98以及Windows95(安装
Winsock2)操作系统中,创建原始套接字时,只能使用IGMP和ICMP。
协议标志
IPPROTO_UDP、IPPROTO_IP以及IPPROTO_RAW均要求使用套接字选项IP_HDRINCL,而
该选项在上述平台下都是不支持的。
然而,Windows2000确实提供了对IP_HDRINCL选项的
支持,所以能够处理IP头(IPPROTO_RAW)、TCP头(IPPROTO_TCP)以及UDP头
(IPPROTO_UDP)。
采用恰当的协议标志,完成了原始套接字的创建之后,接下来的事情便是在发送及接收
调用中,使用对应的套接字句柄。
创建原始套接字时,IP头会包含在接收到的任何返回数据
中,无论是否设定了IP_HDRINCL选项。
13.2Internet控制消息协议
Internet控制消息协议(ICMP)是便于不同主机间传递简短消息的一种机制。
大多数
ICMP消息都牵涉到主机间通信时发生的一些错误;而其他ICMP消息用于对主机进行查询。
ICMP协议采用的是IP定址机制,因为它本身就是封装在IP数据报内的一种协议。
在图13-1中,
我们展示了ICMP消息的各个字段。
整条ICMP消息封装在一个IP头内。
8位ICMP类型8位ICMP代码16位ICMP检验和
ICMP具体内容(取决于类型和代码)
图13-1ICMP头
其中,第一个字段指定的是ICMP消息类型,可分为查询或错误两类。
随后,“代码”字
段进一步定义了查询或消息的类型。
而“校验和”字段的长度为16位,是对ICMP头内容的一
个补余求和。
最后,ICMP的实际内容要依赖于前面设定的ICMP类型及代码。
在表13-1中,
我们对那些类型及代码进行了详细总结。
若生成的是一条ICMP错误消息,那么在消息中,肯定包含了导致那个ICMP错误的IP头,
以及IP数据报的头8个字节。
这样一来,收到ICMP错误的主机便可将消息与一种特定的协议
联系起来。
并可根据实际情况,对那个错误作出处理。
就我们目前的情况来说,Ping需要依
赖于回应请求及回应答复这两种ICMP查询,而不是仅仅依赖一条错误消息。
生成ICMP消息
的主机会通过TCP或UDP,对问题作出响应;除此之外,ICMP的用处并不大。
在下一节里,
342
计计第二部分附WinsockAPI
下载
我们打算讨论如何随原始套接字一道,用ICMP协议来生成一个Ping请求,Ping请求是用来回
应请求和回应答复消息的。
表13-1ICMP消息类型
类型查询/错误(错误类型)代码说明
0查询0回应答复(EchoReply)
3错误:
目标不可抵达0网络访问不到
1主机访问不到
2协议访问不到
3端口访问不到
4数据包需要分段(分解),但却没有设置分段位
5源路由失效
6目标网络未知
7目标主机未知
8源主机独立(作废)
9目标网络管理性禁止
10目标主机管理性禁止
11针对特定的TOS(服务类型),网络不可抵达
12针对特定的TOS(服务类型),主机不可抵达
13由于过滤,通信被管理性地禁止
14违反主机优先级设定
15优先级失效
4错误0源主机停止工作
5错误:
重定向0为网络重定向
1为主机重定向
2和TOS和网络重定向
3和TOS和主机重定向
8查询0回应请求(EchoRequest)
9查询0路由器广告
10查询0路由器请求
11错误:
超时0传输过程中TTL的值变成0
1重新组合时TTL的值变成0
12错误:
参数问题0IP头损坏
1必需的选项丢失
13查询0时间戳请求
14查询0时间戳答复
15查询0信息请求
16查询0信息答复
17查询0地址掩码请求
18查询0地址掩码答复
13.2.1Ping示例
我们经常用Ping来判断一个特定的主机是否处于活动状态,并且是否可以通过网络访问
到。
通过生成一个ICMP“回应请求”(EchoRequest),并将其定向至打算查询的目标主机,
便可知道自己是否能成功地访问到那台机器。
当然,这样做并不能担保一个套接字客户机能
与那个主机上的某个进程顺利地建立连接(例如,远程服务器上的一个进程也许并没有进入
监听模式)。
若Ping成功,那么它只能证明一件事情:
远程主机的网络层可对网络事件作出响
下载
第13章计原始套接字计
计343
应!
概括地说,我们的这个Ping示例需要采取下面这些步骤:
1)创建类型为SOCK_RAW的一个套接字,同时设定协议IPPROTO_ICMP。
2)创建并初始化ICMP头。
3)调用sendto或WSASendto,将ICMP请求发给远程主机。
4)调用recvfrom或WSARecvfrom,以接收任何ICMP响应。
发出ICMP回应请求以后,远程机器会拦截这个请求,然后生成一条回应答复消息,再通
过网络传回给我们。
假如出于某些方面的原因,不能抵达目标主机,就会生成对应的ICMP错
误消息(比如“目标主机访问不到”),由原先打算建立通信的那个路径上某处的一个路由器
返回。
假定与远程主机的物理性连接并不存在问题,但远程主机已经关机或没有设置对网络
事件作出响应,便需由自己的程序来执行超时检定,来侦测出这样的情况。
程序清单13-1列
出的Ping.c示例向大家清晰展示了如何创建一个特殊的套接字,以便实现ICMP包的收发;以
及如何通过IP_OPTIONS套接字选项,来实现记录路由选项。
程序清单13-1Ping.c
344
计计第二部分附WinsockAPI
下载
下载
第13章计原始套接字计
计345
346
计计第二部分附WinsockAPI
下载
下载
第13章计原始套接字计
计347
348
计计第二部分附WinsockAPI
下载
下载
第13章计原始套接字计
计349
350
计计第二部分附WinsockAPI
下载
下载
第13章计原始套接字计
计351
对这个Ping示范程序来说,它最引人注目的一个特点便是采用了IP_OPTIONS套接字选项。
之所以要使用记录路由IP选项,是由于这样一来,当我们的ICMP包抵达路由器时,它的IP地
址便能自动添加到IP选项头内—具体插入位置则取决于事先在IP选项头内设好的偏移量字段。
每次遇到一个路由器向其中加入自己的IP地址时,这个偏移距离都会自动递增4。
之所以是“4”,而不是其他数字,是由于对IPv4地址来说,它的长度正好是4个字节。
本书不打算对IPv6的情况作深入探讨,目前也没有任何一种Windows平台提供了对它的支持。
一旦收到回应答复,便可对选项头进行解析,列印出中途访问过的那些路由器的IP地址及主机名。
大家可参考第9章的内容,了解还能使用其他什么类型的IP选项。
13.2.2Traceroute示例
对IP网络来说,另一个非常有用的工具是Traceroute(路由追踪)。
在Windows操作系统中,
一般可以直接运行Tracert.exe,来调用这个工具。
利用它,我们可侦测出为抵达网络内任何一
个指定的主机,中途需经过哪些路由器,以及它们的IP地址是什么。
当然,亦可利用Ping,
使用IP选项头内的记录路由选项,侦测中途经过的路由器IP地址。
然而,Ping最多只支持9次
“跳跃”—这是在选项头内为地址分配的最大空间。
一个IP数据报需要穿越多个物理性的网
络时,每通过一个路由器,我们就说进行了一次“跳跃”。
若网络较大(如Internet),穿过的
路由器不止9个,便应换用Traceroute。
Traceroute的设计原理是令其向目的地发送一个UDP数据包,并重复递增IP的“存活时间”
(TTL)值。
最开始的时候,TTL等于1;也就是说,一旦它抵达路途中的第一个路由器,TTL
首先会超时(变成0)。
这样便会造成路由器生成一个ICMP“超时”数据包。
随后,最初的
TTL值递增1,以便UDP包能继续传到下一个路由器,而生成的ICMP超时包会自第一个路由
器返回。
只需将返回的每一条ICMP消息都收集下来,便能为中途经过的路由器IP地址勾勒出
一个清晰的轮廓,直到最终抵达目标主机。
一旦TTL的值递增得足够大,可以实际抵达目标
位置,通常便会返回一条ICMP“端口访问不到”消息,因为在接收端主机上,并没有进程在
等待这条消息。
之所以认为Traceroute是一个有用的工具,是由于它为我们提供了与中途经历的路由器有
352
计计第二部分附WinsockAPI
下载
关的大量信息。
进行多播通信,或者在遇到路由问题的时候,这些资料便显得非常宝贵。
对
具体的应用程序来说,尽管需要执行Traceroute的机会比Ping少得多,但对某些特定的任务而
言,却恐怕只有Traceroute才能胜任。
有两个办法可用来实现Traceroute程序。
第一个办法,可使用UDP包和发送数据报,连续
递增更改TTL的值。
TTL每一次“超时”,都会向我们返回一条ICMP消息。
这种方法要求使用
安装了UDP协议的一个套接字来发送数据,同时用安装了ICMP协议的另一个套接字来接收数
据(读取返回的消息)。
这个UDP套接字本身是一个极为普通的UDP套接字,如大家在第7章
中所见。
而ICMP套接字的类型比较特殊,是SOCK_RAW(原始套接字),并设定了
IPPROTO_ICMP协议。
UDP套接字的TTL值需要通过IP_TTL套接字选项加以控制。
此外,也
可以创建一个UDP套接字,并使用IP_HDRINCL选项(本章稍后还会详述),在IP头内对TTL
进行人工设置。
当然,这要求程序员进行更多的工作。
第二个办法,我们只需将ICMP数据包简单地发给目的地,同时连续递增更改TTL的值。
在TTL“超时”的时候,这样做也会造成一条ICMP错误消息的返回。
注意这种方法和Ping有
着某些共通之处,它也只要求使用一个套接字(安装ICMP协议)。
在本书配套光盘的示范代
码文件夹下,大家可找到一个使用了ICMP数据包的Traceroute例子。
本章正文并不打算列出
它的完整源码,因为它的大部分代码都与前面的Ping例子相同。
13.3Internet组管理协议
Internet组管理协议(IGMP)由IP多播通信专用,可对多播组的成员加入或脱离组进行管
理。
大家可参考第11章的内容,了解如何通过Winsock来加入及离开一个多播组。
某个应用程
序加入了一个多播组后,便会向本地网络的每个路由器都发出一条IGMP消息,使用特殊地址
224.0.0.2,该地址固定对应于“所有路由器”组。
根据这条IGMP消息,路由器便可知道以后
发给指定多播地址的数据需要转发给这个新加入的成员。
若没有这项能力,多播数据便和广
播数据没什么区别了。
现在来澄清一些事实。
IGMP协议共有两个版本:
版本1(IGMPv1)和版本2(IGMPv2),
分别由RFC1112和RFC2236文件加以定义。
两者的主要区别在于:
版本1没有提供专门的方
法,让一个主机指示路由器停止转发送给一个多播组的数据。
换言之,若某个主机决定脱离
组成员关系,便没有办法将这种情况通报给路由器,而路由器当然会坚持不懈地向其转发数
据,直至在其发出一个组查询后,发现对方没有“应答”。
在协议第2版中,则增加了一条能
够明确表明自己要“离开”的消息。
这样一来,一旦打算放弃成员资格,主机便可向路由器
发出通知,使其能够立即知道这一情况。
除此以外,两个版本的头格式也存在一些不同。
图
13-2展示了版本1的头结构,图13-3则是版本的2头结构。
由于长度仅为8字节,所以这两个头
其实都非常简单。
4位IGMP版4位IGMP类型8位长度未用16位IGMP检验和
32位多播地址(D类IP地址)
图13-2IGMPv1头格式
下载
第13章计原始套接字计
计353
8位IGMP类型8位最长响应时间16位IGMP检验和
32位多播地址(D类IP地址)
图13-3IGMPv2头格式
其中,版本1的头含有两个4位字段。
第一个字段指定IGMP版本,而第二个字段指定
IGMP消息类型。
在版本2中,单独一个8位字段代替了这两个字段。
此外,版本1中未用的字
段在新版本中派上了用场,用来指定最长的响应时间。
只有在成员关系查询消息中,“最长响
应时间”字段才有意义:
它代表在发出一个响应报告之前,允许等候的最长时间;采用的单
位是0.1秒。
在其他所有消息中,发送都会将该字段设为0,而接收者也会忽略它。
对IGMPv1来说,版本字段必然设为1,而类型字段有两个可选的值,如表13-2所示。
使
用主机成员资格查询(0x1)消息,路由器可判断一个主机正在使用哪些多播组。
在这种情况
下,组地址字段的值应设为0。
路由器会将查询数据包发给“所有主机”地址(224.0.0.1)。
假如有主机仍是某个组的成员,那么各个主机都会将一个主机成员资格报告(IGMP消息类型
0x2)反馈回“所有路由器”地址,同时指出自己加入的是哪个多播地址。
假如一个主机是首
次加入一个组,那么一条组成员资格报告消息也会发给“所有路由器”组。
如表13-3所示,IGMP的第2版加入了两种新的消息类型—版本2成员资格报告(0x16),
以及离开组(0x17)消息。
注意另两类消息在作用上与版本1是相同的(主机成员资格查询和
成员资格报告),尽管为其分配的值不同。
然而,请注意版本2的IGMP包已将版本及类型字段
浓缩到单独一个字段里,而0x11展开成二进制,便是00010001。
从表面看,完全相当于“版
本等于1,类型等于1”!
表13-2IGMP版本1的消息类型
类型说明
0x1主机成员资格查询
0x2主机成员资格报告
表13-3IGMP版本2的消息类型
类型说明
0x11成员资格查询
0x12版本1成员资格报告
0x16版本2成员资格报告
0x17离开组
版本2的成员资格查询(0x11)与版本1的成员资格查询差别是极其微小的。
前者不仅能
进行版本1那样的标准查询,也能在一个网络上,查询在一个指定的组地址中,包括了哪些组
成员。
具体的做法是向“所有路由器”组发送一个成员资格查询包,同时在组地址字段中指
定一个打算查询的目标组。
版本2的成员资格报告则是最新设计的,因为它允许使用“最长超
时”字段,对主机响应查询的时间作出限制。
如超过时间仍未作出响应,便会禁止向其转发
特定多播组的数据。
354
计计第二部分附WinsockAPI
下载
13.4IP_HDRINCL的使用
对原始套接字来说,它存在的一项限制在于,我们只能对已经定义好的协议进行操作,
比如IGMP和ICMP等等。
不能用IPPROTO_UDP来创建一个新的原始套接字,也不能对UDP
头进行操作;TCP也是一样的。
要想对IP头进行操作,同时也能操作TCP或UDP头(或封装在
IP内的其他任何协议),必须随原始套接字一道,使用IP_HDRINCL这个套接字选项。
利用该
选项,我们除了能自行构建IP头,亦能构建其他协议头。
此外,假如想在应用程序中实现自己的协议方案,并将其封装到IP内,那么首先可以创
建一个原始套接字,然后将协议类型设为IPPROTO_RAW。
这样一来,我们就可在IP头内人
工设置协议字段,同时构建自己的定制协议头。
在本小节内,我们打算讲述如何构建自己的
UDP数据包,使大家对其中牵涉到的步骤有一个比较全面的了解。
一旦理解了如何操作UDP
头,再来创建自己的协议头,或对IP内封装的其他协议进行处理,便只是“小菜一碟”。
使用IP_HDRINCL选项时,必须针对每一次发送调用,向IP头内自行填充内容。
同时,
还要填写封装在其中的其他任何协议头。
IP头的结构已在第9章进行了讲述。
大家可参考图
9-3,以及专门讲述IP_HDRINCL选项的那一小节。
与IP相比,UDP头要简单得多。
长度仅
为8个字节,而且只包含了四个字段,如图13-4所示。
其中,头两个字段分别对应源和目标
端口号,长度各自为16位。
第三个字段对应UDP长度;以字节为单位,指定UDP头以及数据
的总长。
第四个字段则是校验和,稍后还会对此详加解释。
UDP包的最后一部分便是实际的
数据。
16位源端口16位目标端口
16位UDP长度16位UDP检验和
图13-4UDP头的格式
由于UDP是一种不能保证数据可靠传输的协议(即“不可靠”协议),所以校验和的计算
是可选的。
然而,为保持大家知识系统的完整性,在此还是要对其进行解释。
与IP校验和不
同,UDP校验和除覆盖了UDP头之外,还同时覆盖了实际的数据,此外还包括IP头的一部分。
而IP校验和只针对IP头进行计算。
要求用于计算UDP校验和的附加字段叫作“伪头”。
一个伪
头由下述项目构成:
■32位源IP地址(IP头)。
■32位目标IP地址(IP头)。
■8位字段(零除外)。
■8位协议。
■16位UDP长度。
加入这些项目的是UDP头以及数据。
校验和的计算方法和IP与ICMP的方法是相同的:
得
出16位1的求余总和。
由于数据可能是个奇数,所以有时需要在数据末尾填充一个0字节,以
便顺利计算出校验和。
这个填充字段并不作为数据的一部分传递。
在图13-5中,我们向大家
演示了计算校验和所需的全部字段。
头三个32位字构成了UDP伪头。
紧接着的是UDP头及其
数据。
要注意的是,由于校验和是以16位值为基础计算出来的,所以或许需要用一个0字节对
数据进行填充。
下载
第13章计原始套接字计
计355
32位源IP地址
32位目的IP地址
08位协议16位UDP长度
16位源端口16位目标端口
16位UDP长度16位UDP检验和
数据(任意字节)
图13-5UDP伪头
程序清单13-2列出的示范程序作用很简单,从我们选择的任何IP及端口,它向任何指定的
目标IP及端口地址发送一个UDP数据包。
第一步是创建一个原始套接字,然后设置
IP_HDRINCL标志:
注意,我们现已创建了一个协议为IPPROTO_UDP的原始套接字。
这种做法只适用于
Windows2000,因为它同时也要求设置IP_HDRINCL选项。
示范程序Iphdrinc.c向大家阐释了
如何利用原始套接字和IP_HDRINCL选项,操作传出去的数据包的IP与UDP头。
程序清单13-2原始UDP示例
356
计计第二部分附WinsockAPI
下载
下载
第13章计原始套接字计
计357
358
计计第二部分附WinsockAPI
下载
下载
第13章计原始套接字计
计359
360
计计第二部分附WinsockAPI
下载
下载
第13章计原始套接字计
计361
建好套接字,并设置了IP_HDRINCL选项之后,代码会开始IP头的填写。
大家可注意到,
代码将IP头声明为一个结构:
I
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- WINDOWS 网络 编程 技术