Недавно я создала небольшой инструмент под названием dnspeep, который позволяет понять, какие DNS-запросы отправляет ваш компьютер и какие ответы он получает. Всего мой код занял 250 строк на Rust. В этой статье я расскажу о коде, объясню, для чего он нужен, почему в нём возникла необходимость, а также расскажу о некоторых проблемах, с которыми я столкнулась при его написании. И, конечно, вы сами сможете попробовать код в действии.
Что нужно для начала работы с кодом
Чтобы было удобнее работать с кодом, я подготовила несколько бинарных файлов.
Команды для Linux (x86):
wget https://github.com/jvns/dnspeep/releases/download/v0.1.0/dnspeep-linux.tar.gz
tar -xf dnspeep-linux.tar.gz
sudo ./dnspeep
Команды для Mac:
wget https://github.com/jvns/dnspeep/releases/download/v0.1.0/dnspeep-macos.tar.gz
tar -xf dnspeep-macos.tar.gz
sudo ./dnspeep
Коду необходим доступ ко всем отправляемым компьютером пакетам DNS, поэтому его необходимо запускать от имени root. По этой же причине утилиту tcpdump также нужно запускать от имени root: код использует libpcap — ту же библиотеку, что и tcpdump. Если вам по какой-либо причине не захочется загружать бинарные файлы и запускать их от имени root, вы можете воспользоваться моим исходным кодом и создать на его основе собственный.
Что получается в результате
Каждая строка представляет собой DNS-запрос и соответствующий запросу ответ.
$ sudo dnspeep
query name server IP response
A firefox.com 192.168.1.1 A: 44.235.246.155, A: 44.236.72.93, A: 44.236.48.31
AAAA firefox.com 192.168.1.1 NOERROR
A bolt.dropbox.com 192.168.1.1 CNAME: bolt.v.dropbox.com, A: 162.125.19.131
Эти запросы отражают мои визиты на сайт neopets.com в браузере, а запрос bolt.dropbox.com возник потому, что у меня запущен агент Dropbox, и, как я полагаю, время от времени он заходит на свой сайт для синхронизации.
Зачем создавать ещё один инструмент DNS?
Я считаю, что в таком инструменте есть смысл, так как с его помощью можно лучше представить, как работает DNS — без этого инструмента довольно сложно понять, как ведёт себя DNS на компьютере.
Ваш браузер (и другие компьютерные программы) постоянно отправляет DNS-запросы. Если знать, какие запросы отправляет компьютер и какие ответы он получает, можно получить более реальную картину "жизни" компьютера.
Написанный мною код я также использую как инструмент отладки. На вопросы типа: "А не связана ли моя проблема с DNS?" ответить иногда бывает довольно сложно. Пользователи, не зная всей информации, зачастую, чтобы найти проблему, используют метод проб и ошибок или просто строят догадки, хотя, казалось бы, стоит просто проанализировать ответы на получаемые компьютером DNS-запросы, и проблема будет решена.
Можно увидеть, какое программное обеспечение "тайно" выходит в Интернет
Мне нравится в этом инструменте, что он даёт представление о том, какие программы моего компьютера пользуются Интернетом! Например, я обнаружила, что какая-то утилита на моём компьютере время от времени по непонятной причине отправляет запросы на ping.manjaro.org, вероятно, чтобы проверить, подключён ли мой компьютер к Интернету.
Один мой приятель с помощью этого инструмента обнаружил, что на его компьютере было установлено корпоративное ПО для мониторинга, которое он просто забыл удалить, когда увольнялся с прошлого места работы.
Если вы раньше не работали с tcpdump, поначалу может быть непонятно, что делает эта утилита
Рассказывая людям об отправляемых с компьютера DNS-запросах, я почти всегда хочу добавить: "Всю информацию можно получить через tcpdump!" Что делает утилита tcpdump? Она осуществляет разбор пакетов DNS! Например, вот как выглядит DNS-запрос incoming.telemetry.mozilla.org.:
11:36:38.973512 wlp3s0 Out IP 192.168.1.181.42281 > 192.168.1.1.53: 56271+ A? incoming.telemetry.mozilla.org. (48)
11:36:38.996060 wlp3s0 In IP 192.168.1.1.53 > 192.168.1.181.42281: 56271 3/0/0 CNAME telemetry-incoming.r53-2.services.mozilla.com., CNAME prod.data-ingestion.prod.dataops.mozgcp.net., A 35.244.247.133 (180)
Сначала эта информация может показаться "тёмным лесом", но её при некоторых навыках можно научиться читать. Давайте для примера разберём такой запрос:
192.168.1.181.42281 > 192.168.1.1.53: 56271+ A?
incoming.telemetry.mozilla.org. (48)
A? означает DNS-запрос типа A;
incoming.telemetry.mozilla.org. — это имя объекта, к которому осуществляется запрос;
56271 — это идентификатор DNS-запроса;
192.168.1.181.42281 — исходный IP/порт;
192.168.1.1.53 — IP/порт назначения;
(48) — длина DNS-пакета.
Ответ выглядит следующим образом:
56271 3/0/0 CNAME telemetry-incoming.r53-2.services.mozilla.com.,
CNAME prod.data-ingestion.prod.dataops.mozgcp.net., A
35.244.247.133 (180)
3/0/0 — количество записей в ответе: 3 ответа, 0 полномочий, 0 дополнительно. Исходя из моего опыта, tcpdump выводит только количество ответов на запрос.
CNAME telemetry-incoming.r53-2.services.mozilla.com, CNAME prod.data-ingestion.prod.dataops.mozgcp.net. и A 35.244.247.133 — это те самые три ответа
56271 — идентификатор ответов, соответствующий идентификатору запроса. По этому идентификатору можно понять, что это ответ на запрос из предыдущей строки.
С таким форматом, конечно, работать довольно сложно, ведь человеку нужно просто посмотреть на трафик DNS, к чему все эти нагромождения? И вот ему приходится вручную сопоставлять запросы и ответы, к тому же не всегда находящиеся на соседних строках. Здесь в дело вступают компьютерные возможности!
Я решила написать небольшую программку — dnspeep, которая будет сама выполнять такое сопоставление, а также удалять определённую (на мой взгляд, лишнюю) информацию.
Проблемы, с которыми я столкнулась при написании кода
При написании кода я столкнулась с рядом проблем.
Мне пришлось несколько изменить библиотеку pcap, чтобы заставить её правильно работать с Tokio на Mac OS — вот эти изменения. Это была одна из тех ошибок, на поиск которой ушло много часов, а всё исправление уложилось в одну строку.
Разные дистрибутивы Linux, по всей видимости, используют разные версии libpcap.so, поэтому мне не удалось воспользоваться непосредственно бинарным файлом, динамически компонующим libpcap (у других пользователей возникала такая же проблема, например здесь). Поэтому компилировать библиотеки libpcap в инструмент на Linux мне пришлось статически. Я до сих пор не понимаю, как такое правильно организовать на Rust, но я добилась, чего хотела, и всё заработало — я скопировала файл libpcap.a в каталог target/release/deps, а затем просто запустила cargo build.
Используемая мною библиотека dns_parser поддерживает не все типы DNS-запросов, а только некоторые самые распространённые. Возможно, для разбора DNS-пакетов можно было использовать другую библиотеку, но я не нашла подходящей.
Поскольку интерфейс библиотеки pcap выдает набор "голых" байтов (в том числе для данных Ethernet-фреймов), мне пришлось написать код, который определял, сколько байтов нужно отсечь от начала строки, чтобы получить IP-заголовок пакета. Но я уверена, что в моей задаче ещё остались "подводные камни".
Кстати, вы даже не представляете, каких сложностей мне стоило подобрать название для своей утилиты — ведь инструментов для работы с DNS великое множество, и у каждого своё название (dnsspy! dnssnoop! dnssniff! dnswatch!) Сначала я хотела включить в название слово spy (шпион) или его синонимы, а затем остановилась на показавшемся мне забавным названии, которое — о чудо! — ещё никто не занял для собственного DNS-инструмента.
У моей утилиты есть один недостаток: она не сообщает, какой именно процесс отправил DNS-запрос, но выход есть — используйте инструмент dnssnoop. Этот инструмент работает с данными eBPF. Судя по описанию, он должен работать нормально, но я его ещё не пробовала.
В моём коде наверняка ещё много ошибок
Мне удалось его протестировать только на Linux и Mac, и я уже знаю как минимум об одной ошибке (из-за того, что поддерживаются не все DNS-запросы). Если найдёте ошибку, пожалуйста, сообщите мне!
Сами по себе ошибки не опасны — интерфейс libpcap доступен только для чтения, самое худшее, что может случиться, — он получит какие-то непонятные данные и выдаст ошибку или аварийно завершит работу.
Мне нравится составлять небольшие учебные материалы
В последнее время я получаю огромное удовольствие от написания небольших учебных материалов по DNS. Вот ссылки на мои предыдущие статьи:
Простой способ составления DNS-запросов;
Рассказывается, что происходит внутри компьютера при отправке DNS-запроса;
Ссылка на dnspeep.
Ранее в своих постах я просто объясняла, как работают существующие инструменты (например dig или tcpdump), а если и писала собственные инструменты, то довольно редко. Но сейчас для меня очевидно, что выходные данные этих инструментов довольно туманны, их сложно понять, поэтому мне захотелось создать более дружественные варианты просмотра такой информации, чтобы не только гуру tcpdump, но любые другие пользователи могли понять, какие DNS-запросы отправляет их компьютер.
А если вы хотите не только понимать, что происходит с запросами и ответами DNS на вашем компьютере, но и писать собственные протоколы для своих приложений, обратите внимание на профессии C++ разработчик или Java-разработчик, позволяющие с нуля освоить эти языки или прокачать своё владение ими. Приходите — опытные менторы и эксперты своего дела с удовольствием поделяться с вами своими знаниями.
Узнайте, как прокачаться и в других специальностях или освоить их с нуля:
Другие профессии и курсы
ПРОФЕССИИ
КУРСЫ
chemtech
Дополню про DNS
Dog — это красивый DNS-клиент командной строки для поиска DNS, который работает как dig. Он имеет красочный вывод, понимает обычный синтаксис аргументов командной строки, поддерживает протоколы DNS-over-TLS и DNS-over-HTTPS и может генерировать JSON.
dnsmeter — это инструмент для тестирования производительности сервера имен и инфраструктуры вокруг него. Он генерирует DNS-запросы и отправляет их через UDP на целевой сервер имен и считает ответы.