一、概述ping及tracert命令
Win 9x的网络命令中,ping.exe及tracert.exe是DOS环境下的命令,前者检测本机H到远程主机R的连通性,后者显示H到R的连接路由。
两者指向R的参数,可为IP地址,如211.68.32.1,也可为域名,如e250.biti.edu.cn.算法,都是从H向R发送要求回应的ICMP试探包p,静等回应,然后,ping作出简单连通与否结论,tracert作出详细路经节点结论。
两者也可将上述参数,置为127.0.0.1或"系统特性"中的机器名,本机自连.
ICMP是IP层的协议,此层只能建立无连接的传输系统,不保证数据包能到达目的地,或按发送顺序到达,不保证H到R的每次连接,路经节点都相同。
IP包的头部,含有H的源节点IP地址,及R的目的节点IP地址.ICMP包,再含IPINFO及ECHO结构:
typedef struct{
u_char TTL; //Time To Live
...
}IPINFO;
typedef struct{
...
u_long Status; //IP status
...
}ECHO;
IPINFO的TTL,决定p能跨过的中途节点数(叫hob_step);ECHO的Status,值为0时,表明到达R.
遵守TCP/IP协议的网络节点,其IP层进程,收到p时的动作是:
(1.1) 中途节点i捕到p时,先对上述TTL域,做TTL=TTL-1,若新TTL值大于0,i就在与它相邻的物理节点中,选择p还未到过的某节点,向其传输带有新TTL值的p,使p不走回头路.若新TTL值等于0,i就向H回发不必回应的ICMP"超时"包e,表明hob_step在中途耗尽,此包的源节点为i,目的节点为H,ECHO.Status为0x2AFB。
(1.2) R捕到p时,同样向H回发e包,此时,源节点变为R,ECHO.Status变为0.
ping仅处理一次回应:根据e的ECHO.Status是否为0,作出连通与否结论。
tracert处理多次回应:由ECHO.Status不为0的e,记下发出e的各中途节点i,直至ECHO.Status变为0,或H已发出最大hob_step=255,仍连不上R,这时,全面试探才算完,作出试探过程涉及的连接路由。
二、单进程tracert.exe化为多线程ping.exe运行
ping发出p时,置TTL值为最大值255.
tracert第一次发出p时,置TTL为1,每次收到e包,就查ECHO.Status值,非0表明TTL太小,够不着目标,这时,它就做p上的TTL=TTL+1,使中途节点能多跨一个,如此发出hob_step递增的p,直至ECHO.Status变为0,或p上的TTL超过255。
笔者在2k下,试探163.com,执行批处理文件
time /t
tracert 163.com
time /t
花费2分钟,显出15行结论。
由此设想,把一个tracert进程的15次TTL赋新值,改为顺序发出TTL=[1-15]的各工作线程Ci,它们都按ping的仅处理一次回应方式工作,都回发e包给主进程,然后自行终止,这样,主进程先记载来自Ci的e包结果Ei,再输出E1,E2,...,E15组成的连接路由。
ICMP.DLL的输出函数IcmpSendEcho,能检测H到R的连通性,于是,笔者写出以下3特点的仿ping及tracert的ping_tracert.c程序:
(2.1) 做ping时,用TTL值为255的IPINFO结构作为参数,执行IcmpSendEcho,向R发送要求回应的p,以实现ping的功能。
(2.2) 做tracert时,第一次试探,生成IPINFO结构的TTL值从1到7的7个线程,各线程用自己的IPINFO结构作为参数,执行IcmpSendEcho,若某线程到达R,则tracert完成,否则等这批线程终止后,记下这批生成的7个中途节点,再生成TTL值从8到14的7个线程,各线程再执行IcmpSendEcho,如此下去,直至某线程到达R,或生成IPINFO结构的TTL值超过255。
这样,单进程tracert,按一批7个多线程ping.exe运行.试探163.com,只费1分钟.
(2.3) IcmpSendEcho能在包中携带中西文数据,e包,返回原数据.
ping_tracert命令行参数为 (-ping|-tracert) host [data],其中-ping将单进程做ping,-tracert将多线程做tracert,host指向R,data是可选项,指向数据。
ping_tracert的开发环境,要求VC6控制台项目的link页,链入ws2_32.lib。
三、运行实例:
ping_tracert -ping 127.0.0.1 abc,得结果
127.0.0.1 xyz Reply:abc
这里,xyz是笔者机器名,ip地址是211.68.35.239
ping_tracert -ping xyz "A 12",得结果
211.68.35.239 xyz Reply:A 12
ping_tracert -tracert 127.0.0.1 "多道 p",得结果
[1] 127.0.0.1 xyz Reply:多道 p
四、源文
#include <stdio.h>
#include <stdarg.h>
#include <winsock2.h>
typedef struct{
u_char TTL; //Time To Live
u_char TOS; //Type Of Service
u_char Flags; //IP flags
u_char OptSz; //Size of options data
u_char FAR *Options; //Options data buffer
}IPINFO,*p_IPINFO;
typedef struct{
u_long Source; //Source address
u_long Status; //IP status
u_long RoundTime; //Round trip time in milliseconds
u_short DataSize; //Reply data size
u_short Reserved; //Unknown
void FAR *pData; //Reply data buffer
IPINFO ipinfo; //Reply options
}ECHO,*p_ECHO;
//ICMP.DLL中的函数指针
HANDLE(WINAPI *p_create)();
BOOL(WINAPI *p_close)(HANDLE);
DWORD(WINAPI *p_jog)(HANDLE,DWORD,LPVOID,WORD,p_IPINFO,LPVOID,DWORD,DWORD);
HANDLE h_icmp;
//远程主机
DWORD host;
struct hostent FAR *p_hostent;
//线程数
#define TOTAL 7
//试探包指针数组
char *stuff[TOTAL];
//数据字节长,试探包字节长,ttl初值
u_char data_len,stuff_len,ttl=1;
//显e的来源及可选数据.参变
u_long ECHO_msg(char ac,...){
p_ECHO p;
va_list vl;
struct in_addr dec;
va_start(vl,ac);
if(ac==2)
printf("[%d]\t",va_arg(vl,short));
p=va_arg(vl,p_ECHO);
dec.S_un.S_addr=p->Source;
//显IP地址
printf("%d.%d.%d.%d",
dec.S_un.S_un_b.s_b1,
dec.S_un.S_un_b.s_b2,
dec.S_un.S_un_b.s_b3,
dec.S_un.S_un_b.s_b4);
p_hostent=gethostbyaddr((char *)&dec.S_un.S_addr,4,PF_INET);
//显域名
if(p_hostent)
printf("\t%s",p_hostent->h_name);
//显可选数据
if(data_len)
printf("\tReply:%.*s\n",p->DataSize,p->pData);
putchar('\n');
va_end(vl);
//标志到达远程主机与否
return p->Status;
}
//子线程执行体
DWORD sub_thread(void *p_arg){
u_char sub_thread_num;
IPINFO ipinfo;
//取当前线程号
sub_thread_num=*(u_char *)p_arg;
memset(&ipinfo,0,sizeof(ipinfo));
//置线程ttl值
ipinfo.TTL=ttl+sub_thread_num;
//向host发送stuff[sub_thread_num],999是超时值
p_jog(h_icmp,host,stuff[sub_thread_num],data_len,&ipinfo,stuff[sub_thread_num
],stuff_len,999);
//终止线程
ExitThread(0);
}
void main(int ac,char *av[]){
HMODULE h_dll;
WSADATA WSA;
u_char thread_no;
//线程参数及线程id数组
DWORD thread_argument[TOTAL],thread_id[TOTAL];
//线程句柄数组
HANDLE h_thread[TOTAL];
IPINFO ipinfo;
if(ac<3){
printf("(-ping|-tracert) host [data]");
return;
}
//映射ICMP.DLL到本进程
h_dll=LoadLibrary("ICMP.DLL");
//取函数指针
p_create=(HANDLE(WINAPI *)())
GetProcAddress(h_dll,"IcmpCreateFile");
p_close=(BOOL(WINAPI *)(HANDLE))
GetProcAddress(h_dll,"IcmpCloseHandle");
p_jog=(DWORD(WINAPI *)
(HANDLE,DWORD,LPVOID,WORD,p_IPINFO,LPVOID,DWORD,DWORD))
GetProcAddress(h_dll,"IcmpSendEcho");
//使用ICMP.DLL各函数前,调用Winsock 1.1版的WSAStartup
WSAStartup(0x101,&WSA);
//解析形如211.68.32.1的av[2]
host=inet_addr(av[2]);
if(host==INADDR_NONE){
//解析形如e250.biti.edu.cn的av[2]
p_hostent=gethostbyname(av[2]);
if(p_hostent)
memmove(&host,p_hostent->h_addr,p_hostent->h_length);
else{
printf("Can't Resolve %s\n",av[2]);
return;
}
}
//建立IcmpSendEcho需要的句柄
h_icmp=p_create();
//若携带数据,就求数据字节长
if(ac==4)
data_len=strlen(av[3]);
//求试探包字节长
stuff_len=data_len+sizeof(ECHO);
if(!strcmp("-ping",av[1])){//单线程执行ping
memset(&ipinfo,0,sizeof(IPINFO));
ipinfo.TTL=255;
stuff[0]=malloc(stuff_len);
//携带数据
memmove(stuff[0],av[3],data_len);
//向host发送stuff[0]
p_jog(h_icmp,host,stuff[0],data_len,&ipinfo,stuff[0],stuff_len,999);
//显e包来源及数据,若未达远程主机,显"unreachable"
if(ECHO_msg(1,(p_ECHO)stuff[0]))
printf("%s:unreachable",av[2]);
}else{//多线程执行tracert
//初始化线程参数数组
for(thread_no=0;thread_no!=TOTAL;thread_no++)
thread_argument[thread_no]=thread_no;
for(;;){
for(thread_no=0;thread_no!=TOTAL;thread_no++){
//某线程TTL值超过255,则不再生成线程
if(ttl+thread_no==256)
break;
stuff[thread_no]=malloc(stuff_len);
//携带数据
memmove(stuff[thread_no],av[3],data_len);
//创建子线程,参数是线程号
h_thread[thread_no]=CreateThread(NULL,0,
(LPTHREAD_START_ROUTINE)sub_thread,&thread_argument[thread_no],0,&thread_id[thread_no]);
}
//等这批线程全终止
WaitForMultipleObjects(thread_no,h_thread,TRUE,INFINITE);
for(ac=0;ac!=thread_no;ac++)
//显e包来源及数据,若有一线程到达远程主机,则不再显
if(!ECHO_msg(2,(short)ttl+ac,(p_ECHO)stuff[ac]))
break;
//ECHO.Status变为0,或TTL超过255,则tracert完成
if(ac!=thread_no || thread_no!=TOTAL)
break;
ttl+=TOTAL;
}
}
//关闭
p_close(h_icmp);
//停用WS2_32.DLL
WSACleanup();
//解射
FreeLibrary(h_dll);
}
|