Windows自带的Tracert是向远程主机发送ICMP包进行追踪,但是目前很多主机关闭了ICMP答复,这个工具不太好使了~~~~~原理咱知道,正规的Trace不就是发送TTL依次递增的UDP包吗?什么网关和路由敢随意丢弃我们的UDP包而...unit YRecords;
interface
uses
Windows;
const
PACKET_SIZE = 32;
MAX_PACKET_SIZE = 512;
TRACE_PORT = 34567;
LOCAL_PORT = 5555;
type
s32 = Integer;
u32 = DWORD;
u8 = Byte;
u16 = word; PU16 = ^U16;
//
//IP Packet Header
//
PIPHeader = ^YIPHeader;
YIPHeader = record
u8verlen : u8;//4bits ver, 4bits len, len*4=true length
u8tos : u8;//type of service, 3bits 优先权(现在已经被忽略), 4bits TOS, 最多只能有1bit为1
u16totallen : u16;//整个IP数据报的长度,以字节为单位。
u16id : u16;//标识主机发送的每一份数据报。
u16offset : u16;//3bits 标志,13bits片偏移
u8ttl : u8;//生存时间字段设置了数据报可以经过的最多路由器数。
u8protol : u8;//协议类型,6表示传输层是TCP协议。
u16checksum : u16;//首部检验和。
u32srcaddr : u32;//源IP地址,不是‘xxx.xxx.xxx.xxx’的形势哦
u32destaddr : u32;//目的IP地址,同上
end;
//
//ICMP Packet Header
//
PICMPHeader = ^YICMPHeader;
YICMPHeader = record
u8type : u8;
u8code : u8;
u16chksum : u16;
u16id : u16;
u16seq : u16;
end;
unit Unit1;
interface
uses
Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,
Dialogs, StdCtrls, YRecords, winsock2;
type
TForm1 = class(TForm)
ListBox1: TListBox;
Edit1: TEdit;
Button1: TButton;
procedure FormCreate(Sender: TObject);
procedure FormClose(Sender: TObject; var Action: TCloseAction);
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;
var
Form1: TForm1;
implementation
{$R *.dfm}
function DecodeIcmpReply( pbuf: PChar; var seq: s32 ): string;
var
pIpHdr : PChar;
pIcmphdr : PICMPHeader;
sip : string;
ttl : integer;
begin
pIpHdr := pbuf;
sip := inet_ntoa( TInAddr( PIPHeader(pIpHdr)^.u32srcaddr ) );
ttl := PIPHeader(pIpHdr)^.u8ttl;
Inc( pIpHdr, (PIPHeader(pIpHdr)^.u8verlen and $0F) * 4 );
pIcmpHdr := PICMPHeader(pIpHdr);
result := ’’;
if pIcmpHdr^.u8type = 3 then //目的不可达信息,Trace完成
seq := 0;
if pIcmpHdr^.u8type = 11 then //超时信息,正在Trace
result := Format( ’M2s�’, [seq, sip, ttl] );
end;
procedure ErrMsg( msg: string );
begin
MessageBox( 0, PChar(msg), ’Ping Program Error’, MB_ICONERROR );
end;
procedure TForm1.FormCreate(Sender: TObject);
var
wsa : TWSAData;
begin
if WSAStartup( $0202, wsa ) <> 0 then
ErrMsg( ’Windows socket is not responed.’ );
ListBox1.Font.Name := ’Courier New’;
ListBox1.Font.Size := 9;
end;
procedure TForm1.FormClose(Sender: TObject; var Action: TCloseAction);
begin
if WSACleanup <> 0 then
ErrMsg( ’Windows socket can not be closed.’ );
end;
procedure TForm1.Button1Click(Sender: TObject);
const
SIO_RCVALL = IOC_IN or IOC_VENDOR or 1;
var
rawsock : TSocket;
pRecvBuf : PChar;
FromAdr : TSockAddr;
FromLen : s32;
fd_read : TFDSet;
timev : TTimeVal;
sReply : string;
udpsock : TSocket;
ret : s32;
DestAdr : TSockAddr;
pSendBuf : PChar;
ttl, opt : s32;
pHost : PHostEnt;
begin
//创建一个RAWSOCK接收回应ICMP包
rawsock := socket( AF_INET, SOCK_RAW, IPPROTO_ICMP );
FromAdr.sin_family := AF_INET;
FromAdr.sin_port := htons(0);
FromAdr.sin_addr.S_addr := inet_addr(’192.168.1.12’); //换成你的IP
//如果不bind就无法接收包了~~~因为下面还要创建一个UDPSOCK
bind( rawsock, @FromAdr, SizeOf(FromAdr) );
Opt := 1;
WSAIoctl( rawsock, SIO_RCVALL, @Opt, SizeOf(Opt), nil, 0, @ret, nil, nil );
//接收ICMP回应包的缓冲区
pRecvBuf := AllocMem( MAX_PACKET_SIZE );
//创建一个UDPSOCK发送探测包
udpsock := socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
//要发送的UDP数据
pSendBuf := AllocMem( PACKET_SIZE );
FillChar( pSendBuf^, PACKET_SIZE, ’C’ );
FillChar( DestAdr, sizeof(DestAdr), 0 );
DestAdr.sin_family := AF_INET;
DestAdr.sin_port := htons( TRACE_PORT );
DestAdr.sin_addr.S_addr := inet_addr( PChar(Edit1.Text) );
//如果edit1.text不是IP地址,则尝试解析域名
if DestAdr.sin_addr.S_addr = INADDR_NONE then
begin
pHost := gethostbyname( PChar(Edit1.Text) );
if pHost <> nil then
begin
move( pHost^.h_addr^^, DestAdr.sin_addr, pHost^.h_length );
DestAdr.sin_family := pHost^.h_addrtype;
DestAdr.sin_port := htons( TRACE_PORT );
ListBox1.Items.Add( Edit1.Text +’IP地址->’+ inet_ntoa(DestAdr.sin_addr) );
end else
begin
ListBox1.Items.Add( ’解析域名: ’ + Edit1.Text + ’出错。’ );
closesocket( rawsock );
closesocket(udpsock);
FreeMem( pSendBuf );
FreeMem( pRecvBuf );
exit;
end;
end;
ListBox1.Items.Add( ’Trace route ’ + Edit1.Text + ’......’ );
Listbox1.Update;
//开始Trace!!!
ttl := 1;
while True do
begin
//设置TTL,使我们发送的UDP包的TTL依次累加
setsockopt( udpsock, IPPROTO_IP, IP_TTL, @ttl, sizeof(ttl) );
//发送UDP包到HOST
sendto( udpsock, pSendBuf^, PACKET_SIZE, 0, DestAdr, sizeof(DestAdr) );
FD_ZERO( fd_read );
FD_SET( rawsock, fd_read );
timev.tv_sec := 5;
timev.tv_usec := 0;
if select( 0, @fd_read, nil, nil, @timev ) < 1 then
break;
if FD_ISSET( rawsock, fd_read ) then
begin
FillChar( pRecvBuf^, MAX_PACKET_SIZE, 0 );
FillChar( FromAdr, sizeof(FromAdr), 0 );
FromAdr.sin_family := AF_INET;
FromLen := sizeof( FromAdr );
recvfrom( rawsock, pRecvBuf^, MAX_PACKET_SIZE, 0, FromAdr, FromLen );
sReply := DecodeIcmpReply( pRecvBuf, ttl );
if sReply <> ’’ then
begin
ListBox1.ItemIndex := ListBox1.Items.Add( sReply );
Listbox1.Update;
end;
if ttl = 0 then //如果收到目标主机的相应包,DecodeIcmpReply会把ttl==0
break;
end;
Inc( ttl );
Sleep( 110 );
end; //while not bStop do
ListBox1.Items.Add( ’追踪路由完成。’ );
ListBox1.Items.Add( ’ ’ );
closesocket( rawsock );
closesocket(udpsock);
FreeMem( pSendBuf );
FreeMem( pRecvBuf );
end;
end.

http://blog.sina.com.cn/s/blog_562349090100zkvn.html

Delphi用Socket API实现路由追踪的更多相关文章

  1. Delphi的Socket编程步骤(repulish)

    转贴自:http://topic.csdn.net/t/20010727/16/212155.html ClientSocket 和ServerSocket几个重要的属性:   1.client和se ...

  2. Delphi的Socket编程步骤

    ClientSocket 和ServerSocket几个重要的属性:   1.client和server都有port属性,需要一致才能互相通信   2.client有Address属性,使用时填写对方 ...

  3. socket编程 ------ BSD socket API

    伯克利套接字(Berkeley sockets),也称为BSD Socket.伯克利套接字的应用编程接口(API)是采用C语言的进程间通信的库,经常用在计算机网络间的通信. BSD Socket的应用 ...

  4. TCP协议和socket API 学习笔记

    本文转载至 http://blog.chinaunix.net/uid-16979052-id-3350958.html 分类:  原文地址:TCP协议和socket API 学习笔记 作者:gilb ...

  5. JAVA Socket API与LINUX Socket API探究

    代码 这是一个带有UI界面的JAVA网络聊天程序,使用Socket连接完成通信. JAVA服务端程序 import java.io.IOException; import java.io.InputS ...

  6. delphi的socket通讯 多个客户端 (转)

    ClientSocket组件为客户端组件.它是通信的请求方,也就是说,它是主动地与服务器端建立连接. ServerSocket组件为服务器端组件.它是通信的响应方,也就是说,它的动作是监听以及被动接受 ...

  7. Creating Your Own Server: The Socket API, Part 2

    转:http://www.linuxforu.com/2011/09/creating-your-own-server-the-socket-api-part-2/ By Pankaj Tanwar  ...

  8. Creating Your Own Server: The Socket API, Part 1

    转:http://www.linuxforu.com/2011/08/creating-your-own-server-the-socket-api-part-1/ By Pankaj Tanwar  ...

  9. Delphi内存操作API函数(备查,并一一学习)

    Delphi内存操作API函数System.IsMemoryManagerSet;System.Move;System.New;System.ReallocMem;System.ReallocMemo ...

随机推荐

  1. 【codeforces 782B】The Meeting Place Cannot Be Changed

    [题目链接]:http://codeforces.com/contest/782/problem/B [题意] 每个人都有一个速度,只能往上走或往下走; 然后让你找一个地方,所有人都能够在t时间内到达 ...

  2. win32命令行小程序获取指定文件夹或者目录下面的所有文件大小,文件数量,目录数量

    #include <Windows.h> #include <stdio.h> #include <tchar.h> LARGE_INTEGER       lgA ...

  3. 集装箱set相关算法

     set_union 算法set_union可构造S1.S2的并集.此集合内含S1或S2内的每个元素. S1.S2及其并集都是以排序区间表示.返回值是一个迭代器.指向输出区间的尾端. 因为S1和S ...

  4. 《node.js开发指南》第五章与新版Node变化太大的一些问题

    1.在win下,命令行工具express -h无效,因为4.x版本的express需要安装express-generator才可以使用express命令,npm install -g express- ...

  5. spring mybatis circular reference

    摘要: Error creating bean with name 'XXX': Requested bean is currently in creation: Is there an unreso ...

  6. New in 10.2.2: C++ and Debugger Improvements

    In RAD Studio 10.2.2, we've made a number of great quality improvements for the C++ toolchain and fo ...

  7. [Linux] ssh秘钥对免密码登陆

    准备两台linux服务器 a和b , 在a上使用ssh命令登陆b服务器 , 并且不用 输入密码 1.在a服务器上,比如是root用户 ,进去/root/.ssh目录 ,没有就创建, 就是进入家目录的. ...

  8. CentOS6.5系统挂载NTFS分区的移动硬盘

    CentOS6.5系统挂载NTFS分区的移动硬盘 作为IT的工作者,避免不了使用Linux系统,我如今使用的系统是CentOS6.5 X86_64位版本号,可是插入NTFS移动硬盘没有办法识别.通过以 ...

  9. Windows搭建Eclipse+JDK+SDK的Android --安卓开发入门级

     一 相关下载 (1) java JDK下载: 进入该网页: http://java.sun.com/javase/downloads/index.jsp (或者直接点击下载)例如以下图: 选择 ...

  10. LeetCode OJ平台Sort Colors讨论主题算法

    原题如下面,这意味着无序排列(由0,1,2组成).一号通.组织成若干阵列0-几个1-几个2这样的序列. Given an array with n objects colored red, white ...