Короче, поломался тут у меня интернет. Ну, традиционно запустил ping, tracert: короче, шлюз "Сибирских сетей", к которому подключен мой роутер, иногда пропадает на несколько десятков минут, хотя физически соединение до коммутатора провайдера живое.
Пишу в тех. поддержку, понятное дело. И тут выясняю, что я отстал от жизни: ни ping, ни tracert нонче у сетевиков не котируются, им нужно специальную программку поставить и её логи выслать в тех. поддержку, что бы они могли проблему решить.
Программка PingPlotter называется. В принципе, хорошая программка, есть у нее только один недостаток: она платная. Хоть и стоит недорого, но все же, особенно в условиях санкций, имеет значение. Да и нужна-то она раз в пять лет, если не реже.
Ну и кстати, логи трассировки, полученные с помощью этой программки никак не помогли решить мою проблему: видимо, надо менять провайдера. Уже больше месяца "Сибирские сети" возятся, вроде на прошлой неделе ушла проблема, но на этой неделе снова вернулась. Хоть и не так критично, как раньше, но все же пару раз в день интернет на несколько минут пропадает.
В общем, в процессе всей этой катавасии, уже традиционно, захотелось и мне какую-то подобную программку замутить. А, думаю, это же просто, сейчас возьму готовый компонентик, да и сделаю.
В Delphi для подобных задач есть набор компонентов Indy, вроде десятой версии они у меня. Да вот незадача: компонентик для отправки ICMP запросов как-то странно работает. То есть просто пинг вроде норм, но как только ставишь маленький TTL, что бы сэмулировать трассировку маршрута, он вываливается со странной ошибкой: маленький размер буфера приема.
Проверил компонент Indy для трассировки, а не для пинга: та же шняга. Все же половина этих компонентов явно не доделана.
Порылся по интернету, нашел готовый модуль, который умеет отправлять ICMP-пинг безо всяких компонентов, используя лишь API Windows: Winsock и специальную библиотеку в довесок. Вполне себе рабочий, но вот только на Win64 он выдает ту же ошибку, что и Indy, а на Win32 норм.
Короче, фишка тут вот в чем. Windows, если в ответ вернулся ICMP-ответ с ошибкой, в буфер приема еще кладет дополнительно к обычной информации первые 8 байт из сообщения об ошибке принятого пакета. Поэтому если ICMP дошел до адресата без ошибки, то компонент Indy работает нормально, а вот если вернулась ошибка, например, "Время жизни (TTL) истекло в пути", то длины приемного буфера не хватает для помещения стандартного ответа ICMP плюс еще и сообщения об ошибки. К сожалению, видимо, разработчики компонентов Indy, видимо, не знали о такой особенности и дополнительно 8 байт в приемный буфер не выделили на случай возврата ошибочного пакета.
Но, удивительно, Win64 пишет в приемный буфер не 8, а 16 байт из сообщения об ошибке. Уж не знаю, почему. Поэтому найденный мною код и не работал в 64-битном режиме.
В общем, исходный файлик маленько подшаманил, добавил условную компиляцию, так что теперь он может работать и в 32-битном и в 64-битном режиме. Выложу здесь, может, кому пригодиться.
По хорошему, его немного надо доработать. Дело в том, что выполнения подобных ICMP задач в Windows добавлена специальная библиотека. Без нее, на голом WinSock, требуется, что бы приложение было запущено с привилегиями администратора, а с такой библиотекой можно пинговать и обычному пользователю.
Вот только на разных версиях Windows библиотека называется по разному: где-то Iphlpapi.dll, а где-то Icmp.dll. Так что по-хорошему, надо грузить библиотеки динамически, определяя, какой из вариантов на данной машине подходит.
Еще один любопытный момент связан с использованием указателей при вызове ICMP запросов. И для 32-битных, и для 64-битных систем используется одна и та же структура данных для вызовов функций, при этом все указатели, остаются, естественно 32-битными.
Как же это работает в 64-битной Windows, где указатели 64-битные? Оказывается, очень просто: 32-битный указатель расширяется (беззнаково) до 64-битного. Используется здесь тот факт, что Windows вроде всегда выделяет память под данные приложения с начала адресного пространства.
Вывод из этого безобразия: пока ваша программа вместе с данными влезает в 4 ГиБ, то все гарантированно будет работать, но как только станет больше - уже не факт ).
Ну, и кстати, в процессе узнал много интересного. Раньше думал, что, как и на Windows, на других операционках пользуются ICMP запросом Echo для пинга и трассировки маршрута. Но, оказывается, на Unix-подобных системах предпочитают отправлять UDP-запросы на неиспользуемый порт, ловя обратно ICMP-ответы по мере прохождения маршрута. Завершение маршрута определяет по ошибке о недоступности порта. В принципе, годный метод, но как быть, если порт на удаленной машине для чего-то используется? Ведь пингуя таким образом удаленную машину, мы не можем знать, какие порты на ней используются, а какие нет.
А еще бывают и более экзотические варианты пинга, на основе TCP. Но в эту сторону особо не смотрел.