Что нового

[Сеть, интернет] Трассировка маршрута (Tracert)

IMStrelcov

CTPEJIbLLOB
Сообщения
258
Репутация
66
Доброго времени форумчане. Помогите реализовать вот этот код (Delphi) в AutoIt-е.
Код оставляю в оригинале с коментами и данными автора.

Код:
    ////////////////////////////////////////////////////////////////////////////////
    //
    //  Демонстрационная программа Tracert.exe
    //  Цель: показать принцип трассировки
    //
    //  Автор: Александр (Rouse_) Багель
    //  mailto: [email protected]
    //
    //  Отдельное спасибо Игорю Шевченко за тестирование кода
    //  и указание на ошибки, которые могут возникнуть при компиляции
    //  в различных версиях Delphi, а также за советы по оптимизации кода
    //
    //  8 апреля 2004 года
    //
    ////////////////////////////////////////////////////////////////////////////////
    //
    //  Как это работает?
    //
    //  Для начала нужно вспомнить формат заголовка IP-пакета,
    //  точнее одно из его полей - TTL (Time To Live).
    //  Это восьмибитное поле задает максимальное число хопов
    //  (hop - "прыжок" - прохождение дейтаграммы от одного маршрутизатора к другому)
    //  в течение которого пакет может находиться в сети.
    //  Каждый маршрутизатор,  обрабатывающий эту дейтаграмму,
    //  выполняет операцию TTL=TTL-1.
    //  Когда TTL становится равным нулю,
    //  маршрутизатор уничтожает пакет,
    //  отправителю высылается ICMP-сообщение Time Exceeded.
    //
    //  Утилита посылает в направлении заданного хоста пакет с TTL=1,
    //  и ждет, от кого вернется ответ time exceeded.
    //  Отвечающий записывается как первый хоп
    //  (результат первого шага на пути к цели).
    //  Затем посылаются последовательно пакеты с TTL=2, 3, 4 и т.д. по порядку,
    //  пока при некотором значении TTL пакет не достигнет цели
    //  и не получит от нее ответ.
    //
    //  © http://www.nvkz.net/taifun/xak/tracert.htm
    //
    ////////////////////////////////////////////////////////////////////////////////
     
    unit uMain;
     
    interface
     
    uses
      Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms,
      Dialogs, StdCtrls, WinSock, Spin;
     
    {$DEFINE NO_MESSAGE}
     
    const
      ICMP = 'ICMP.DLL';
      RES_UNKNOWN   = 'Unknown';
      WSA_TYPE = $101;
      STR_TRACE = 'Трассировка маршрута к ';
      STR_JUMP = 'с максимальным числом прыжков ';
      STR_DONE = 'Трассировка завершена.' + #13#10;
      HOST_NOT_REPLY = 'Превышен интервал ожидания для запроса.';
      
    type
      IP_INFO = packed record
        Ttl: Byte;
        Tos: Byte;
        IPFlags: Byte;
        OptSize: Byte;
        Options: Pointer;
      end;
      PIP_INFO = ^IP_INFO;
     
      ICMP_ECHO = packed record
        Source: Longint;
        Status: Longint;
        RTTime: Longint;
        DataSize: Word;
        Reserved: Word;
        pData: Pointer;
        i_ipinfo: IP_INFO;
      end;
     
      TfrmMain = class(TForm)
        gbTracert: TGroupBox;
        memShowTracert: TMemo;
        edAddr: TEdit;
        btnStart: TButton;
        sedCount: TSpinEdit;
        lblHost: TLabel;
        lblHop: TLabel;
        procedure btnStartClick(Sender: TObject);
      end;
     
      TTraceThread = class(TThread)
      private
        DestAddr: in_addr;
        TraceHandle: THandle;
        DestinationAddress,
        ReportString: String;
        IterationCount: Byte;
      public
        procedure Execute; override;
        procedure Log;
        function Trace(const Iteration: Byte): Longint;
      end;
     
    var
      frmMain: TfrmMain;
     
    implementation
     
    {$R *.dfm}
     
    function IcmpCreateFile: THandle; stdcall; external ICMP name 'IcmpCreateFile';
    function IcmpCloseHandle(IcmpHandle: THandle): BOOL; stdcall;
      external ICMP name 'IcmpCloseHandle';
    function IcmpSendEcho(IcmpHandle : THandle; DestAddress: Longint;
      RequestData: Pointer; RequestSize: Word; RequestOptns: PIP_INFO;
      ReplyBuffer: Pointer; ReplySize, Timeout: DWORD): DWORD; stdcall;
      external ICMP name 'IcmpSendEcho';
     
    { Other functions }
     
    // Функция возвращает имя хоста по его IP адресу
    function GetNameFromIP(const IP: String): String;
    const
      ERR_INADDR    = 'Can not convert IP to in_addr.';
      ERR_HOST      = 'Can not get host information.';
      ERR_WSA       = 'Can not initialize WSA.';
    var
      WSA   : TWSAData;
      Host  : PHostEnt;
      Addr  : u_long;
      Err   : Integer;
    begin
      Result := RES_UNKNOWN;
      Err := WSAStartup(WSA_TYPE, WSA);
      if Err <> 0 then
      begin
        {$IFNDEF NO_MESSAGE}
          MessageDlg(ERR_WSA, mtError, [mbOK], 0);
        {$ENDIF}
        Exit;
      end;
      try
        Addr := inet_addr(PChar(IP));
        if Addr = u_long(INADDR_NONE) then
        begin
          {$IFNDEF NO_MESSAGE}
            MessageDlg(ERR_INADDR, mtError, [mbOK], 0);
          {$ENDIF}
          Exit;
        end;
        Host := gethostbyaddr(@Addr, SizeOf(Addr), PF_INET);
        if Assigned(Host) then
          Result := Host.h_name
        {$IFNDEF NO_MESSAGE}
          else
            MessageDlg(ERR_HOST, mtError, [mbOK], 0)
        {$ENDIF}
        ;
      finally
        WSACleanup;
      end;
    end;
     
    // Функция преобразует IP адрес в его строковый эквивалент
    function GetDottetIP(const IP: Longint): String;
    begin
      Result := Format('%d.%d.%d.%d', [IP and $FF,
        (IP shr 8) and $FF, (IP shr 16) and $FF, (IP shr 24) and $FF]);
    end;
     
    { TfrmMain }
     
    procedure TfrmMain.btnStartClick(Sender: TObject);
    begin
      // Чтобы программа не подвисала
      // запускаем трассировку в отдельном потоке
      with TTraceThread.Create(False) do begin
        FreeOnTerminate := True;
        // Передаем имя хоста
        DestinationAddress := edAddr.Text;
        // и максимальное число прыжков
        IterationCount := sedCount.Value;
        Resume;
      end;
    end;
     
    { TTraceThread }
     
    procedure TTraceThread.Execute;
    var
      WSAData: TWSAData;   // Служебные
      Host: PHostEnt;      // переменные
      Error,               // для просмотра кодов ошибок
      TickStart: DWORD;    // для подсчета времени ответа на пинг
      Result: Longint;     // содержит результат выполнения Trace
      I,                   // для цикла
      Iteration: Byte;     // используется для увеличения TTL
      HostName: String;    // содержит имя хоста
      HostReply: Boolean;  // флаг False если хост не ответил 3 раза на пинг
      HostIP: LongInt;     // при ответе хоста сюда заносится его IP (во избежания глюка)
    begin
      // Инициализируем Winsock
      Error := WSAStartup(WSA_TYPE, WSAData);
      if Error <> 0 then
      begin
        ReportString := SysErrorMessage(WSAGetLastError);
        Synchronize(Log);
        Exit;
      end;
     
      try
        // Пытаемся получить IP адрес
        // до которого будем проводить трассировку
        Host := gethostbyname(PChar(DestinationAddress));
        if not Assigned(Host) then
        begin
          ReportString := SysErrorMessage(WSAGetLastError);
          Synchronize(Log);
          Exit;
        end;
     
        // Запоминаем полученый адрес
        DestAddr := PInAddr(Host.h_addr_list^)^;
     
        // Подготавливаемся к отправке эхозапросов (пинга)
        TraceHandle := IcmpCreateFile;
        if TraceHandle = INVALID_HANDLE_VALUE then
        begin
          ReportString := SysErrorMessage(GetLastError);
          Synchronize(Log);
          Exit;
        end;
     
        try
          // Выводим информационные строки вида:
          // Трассировка маршрута к www.delphimaster.ru [62.118.251.90]
          // с максимальным числом прыжков 30:
          ReportString := STR_TRACE + DestinationAddress
            + ' [' + GetDottetIP(DestAddr.S_addr)+ ']' + #13#10;
          Synchronize(Log);
          ReportString := STR_JUMP + IntToStr(IterationCount) + ':' + #13#10;
          Synchronize(Log);
     
          // Инициализируем переменные
          Result := 0;
          Iteration := 0;
     
          // Начинаем трассировку до тех пор
          while (Result <> DestAddr.S_addr) and // пока IP адреса не совпадут
                (Iteration < IterationCount) do // или кол-во прыжков достигнет максимального
          begin
            Inc(Iteration); // Увеличиваем время жизни пакета
     
            HostReply := False; // Выставляем флаг, "хост пока не ответил"
     
            // Запускаем серию из 3 эхозапросов
            for I := 0 to 2 do
            begin
              TickStart := GetTickCount;  // Для каждого засекаем время
              Result := Trace(Iteration); // Делаем пинг
     
              if Result = -1 then // Если нет ответа выводим звезду
                ReportString := '    *    '
              else
              begin  // Если есть ответ - выводим данные (результатом будет IP ответившего)
                ReportString := Format('%6d ms', [GetTickCount - TickStart]);
                HostReply := True;  // и не забываем выставить флаг
                HostIP := Result;
              end;
     
              if I = 0 then
                ReportString := Format('%3d: %s', [Iteration, ReportString]);
              Synchronize(Log);
            end;
     
            if HostReply then // Если хост ответил хотябы на 1 пинг
            begin
              // Получаем преобразованный в строковый вид IP
              ReportString := GetDottetIP(HostIP);
              // Получаем имя хоста
              HostName := GetNameFromIP(ReportString);
              // Вывод данных в зависимости от того - получено ли имя хоста
              if HostName <> RES_UNKNOWN then
                ReportString := HostName + '[' + ReportString + ']';
              ReportString := ReportString + #13#10;
            end
            else
              ReportString := HOST_NOT_REPLY + #13#10;
     
            ReportString := '  ' + ReportString;
            Synchronize(Log);
          end;
     
        finally
          IcmpCloseHandle(TraceHandle);
        end;
     
        // Выводим информационную строку "Трассировка завершена."
        ReportString := STR_DONE;
        Synchronize(Log);
      finally
        WSACleanup;
      end;
    end;
     
    // Процедура отвечает за вывод информации в memShowTracert
    procedure TTraceThread.Log;
    begin
      frmMain.memShowTracert.Text :=
        frmMain.memShowTracert.Text + ReportString;
      SendMessage(frmMain.memShowTracert.Handle, WM_VSCROLL, SB_BOTTOM, 0);
    end;
     
    // Однократная посылка эхозапроса
    function TTraceThread.Trace(const Iteration: Byte): Longint;
    var
      IP: IP_INFO;
      ECHO: ^ICMP_ECHO;
      Error: Integer;
    begin
      GetMem(ECHO, SizeOf(ICMP_ECHO));
      try
        with IP do // Заполнение заголовка
        begin
          Ttl := Iteration; // Самый важный момент в трассировке -  постепенное увеличение TTL
          Tos := 0;
          IPFlags := 0;
          OptSize := 0;
          Options := nil;
        end;
     
        // Непосредственно посылка эхозапроса
        Error := IcmpSendEcho(TraceHandle,
                              DestAddr.S_addr,
                              nil,
                              0,
                              @IP,
                              ECHO,
                              SizeOf(ICMP_ECHO),
                              5000);
        // Проверка на ошибки
        if Error = 0 then
        begin
          Result := -1;
          Exit;
        end;
     
        // Если ошибок не обнаружено результатом будет IP адрес ответившего хоста
        Result := ECHO.Source;
     
      finally
        FreeMem(ECHO);
      end;
     
    end;
     
    end.
 
Автор
IMStrelcov

IMStrelcov

CTPEJIbLLOB
Сообщения
258
Репутация
66
Помощи не дождался справился сам. Выложу наработки, может кому потребуется. Не судите за код, не было времени привести его в порядок, но он рабочий. (Win 7 x64)

Код:
$IP_Address = 'www.ru'
$Ttl=9
$Time=1000





TCPStartup()
$IP_Address=TCPNameToIP($IP_Address)
TCPShutdown()

$WSOCK32DLL = DllOpen("wsock32.dll")
$ICMPDLL = DllOpen("icmp.dll");Iphlpapi.dll

$dwAddress = DllCall($WSOCK32DLL, "uint", "inet_addr", "str", $IP_Address)
$hPort = DllCall($ICMPDLL, "hwnd", "IcmpCreateFile")

Global Const $ICMP_OPTIONS = _
"ubyte Ttl;" & _
"ubyte Tos;" & _
"ubyte Flags;" & _
"ubyte OptionsSize;" & _
"ptr OptionsData"

Global Const $tagICMP_ECHO_REPLY = _
"ulong Address;" & _ ; IPAddr
"ulong Status;" & _
"ULONG RoundTripTime;" & _
"USHORT DataSize;" & _
"USHORT Reserved;" & _
"ptr Data;" & _
$ICMP_OPTIONS

$_OPTIONS = DllStructCreate($ICMP_OPTIONS)
$_OPTIONS.Ttl=0








$Msg=''
$Timer=0

While $_OPTIONS.Ttl < $Ttl
   $_OPTIONS.Ttl += 1
   $ECHO = DllStructCreate($tagICMP_ECHO_REPLY & ";char[355]")
   $Timer=TimerInit()
   _IcmpSendEcho($hPort[0], $dwAddress[0], 0, 0, DllStructGetPtr($_OPTIONS), DllStructGetPtr($ECHO), DllStructGetSize($ECHO), $Time)
   $Timer=Round(TimerDiff($Timer))
   Switch $ECHO.Status
   Case 11013
	  $Msg='Success'
   Case 11010
	  $Msg='No Success'
   Case 0
	  $Msg='End'
   Case Else
	  $Msg='Unknown'
   EndSwitch
   ConsoleWrite($_OPTIONS.Ttl&' '&$Msg&' '&$Timer&' '&_DecIPToString($ECHO.Address)&@CRLF)
   If Not $ECHO.Status Then
	  ExitLoop
   EndIf
WEnd

DllCall($ICMPDLL, "uint", "IcmpCloseHandle", "hwnd", $hPort[0])











Func _DecIPToString($DecIP)
  Local $IPString =  DllCall("ws2_32.dll","str","inet_ntoa", "uint",$DecIP)
  If @error Then Return SetError(1,"0.0.0.0")
  Return $IPString[0]
EndFunc

Func _IcmpSendEcho($IcmpHandle, $DestinationAddress, $RequestData, $RequestSize, $RequestOptions, $ReplyBuffer, $ReplySize, $Timeout)
   Local $ret = DllCall($ICMPDLL, "dword", "IcmpSendEcho", "hwnd", $IcmpHandle, "uint", $DestinationAddress, "str", $RequestData, "dword", $RequestSize, "ptr", $RequestOptions, "ptr", $ReplyBuffer, "dword", $ReplySize, "dword", $Timeout)
   If @error Then Return SetError(@error+1000, 0, 0)
   Return $ret[0]
EndFunc
 
Верх