Пользователь
0,0
рейтинг
15 января 2013 в 13:20

Разработка → Whois: практическое руководство пользователя из песочницы tutorial

Статья рассказывает о работе whois протокола, о существующих клиентских решениях и об особенностях коммуникации с различными whois серверами (а также о выборе правильного whois сервера). Ее основная задача — помочь в написании скриптов для получения whois информации для IP адресов и доменов.

Что такое whois?


Что такое и для чего нужен whois можно прочитать, например, здесь: http://en.wikipedia.org/wiki/Whois.

В нескольких словах, whois (от английского «who is» — «кто такой») – сетевой протокол, базирующийся на протоколе TCP. Его основное предназначение – получение в текстовом виде регистрационных данных о владельцах IP адресов и доменных имен (главным образом, их контактной информации). Запись о домене обычно содержит имя и контактную информацию «регистранта» (владельца домена) и «регистратора» (организации, которая домен зарегистрировала), имена DNS серверов, дату регистрации и дату истечения срока ее действия. Записи об IP адресах сгруппированы по диапазонам (например, 8.8.8.0 — 8.8.8.255) и содержат данные об организации, которой этот диапазон делегирован.

Часто whois используется для проверки, свободно ли доменное имя или уже зарегистрировано. Теоретически, это можно сделать просто открыв домен в браузере, однако на практике зарегистрированное имя не всегда используется.

Стоит отметить, что все контактные данные вводятся в момент регистрации, и со временем они могут устареть. Каждый регистратор имеет свою собственную процедуру их обновления, кроме того, сам процесс может занять некоторое время (хотя обычно это происходит в течение суток).

Протокол подразумевает клиент-серверную архитектуру и используется для доступа к публичным серверам баз данных (как правило, самих регистраторов IP адресов и доменных имен). В некоторых случаях, whois сервер для определенного домена верхнего уровня содержит полную базу данных обо всех зарегистрированных доменах. В других случаях, такой whois сервер содержит лишь самую базовую информацию и отсылает к whois серверам конкретных регистраторов.

Остальные детали будут рассмотрены по ходу самой статьи. Информации, на самом деле, очень много, поэтому специально для тех, кто хочет получить лишь общее представление, не углубляясь в технические детали, есть ее «выжимка»: можете сразу переходить к последнему разделу статьи, который называется "Короткие итоги".

Небольшая предыстория


Есть программный продукт, Интернет-портал, который предоставляет различные сведения об IP адресах и доменах, в том числе whois информацию. Испокон веков для этих целей использовалась сторонняя утилита jwhois, в работу которой никто не вникал. Однако после миграции продукта на новую версию FreeBSD неожиданно оказалось, что jwhois перестал работать: для большинства доменов начала выдаваться абсолютно нелогичная ошибка «Unable to connect to remote host». Google не помог, и я уже был готов начинать дебажить C-шный код, как вдруг оказалось, что jwhois нам вообще не подходит из-за своей лицензии (GPL v3). В общем, встала задача поиска какого-то альтернативного решения.

К моему удивлению, каких-либо вменяемых альтернатив в наличии не оказалось. Большинство форумов как раз и рекомендовали использовать тот самый jwhois. Несколько программ, конечно, нашлось (в том числе несколько библиотек на родном для нашего продукта языке Python), однако большинство из них были забракованы уже буквально после первого же теста на домен в зоне «ua». В целом, все решения выглядели откровенно написанными «на коленке» и абсолютно неподходящими для серьезного продукта. Единственная библиотека, которая на тот момент вызывала доверие, была написана на Ruby (что совсем нам не подходило) и имела пугающий размер исходников и различных конфигурационных файлов для каждого конкретного whois сервера.

Оставалась еще стандартная Unix-овая утилита, так и называющаяся «whois», однако и ее работа оставляла желать лучшего.

В общем, единственным вариантом было садиться читать спецификации по протоколу и писать решение самому.

Результатом этого стал модуль на языке Python на 1000 строк кода, в процессе написания которого я последовательно наступил на все сопутствующие грабли, и, в конечном итоге, данная статья, которая про все эти «грабли» и рассказывает (да, если что, то код проприетарный).

Забегая наперед, замечу, что с высоты приобретенного опыта и jwhois, и Ruby Whois (http://www.ruby-whois.org) теперь также выглядят «оставляющими желать лучшего».

В чем, собственно, проблема?


Вся работа whois описана в RFC 3912 (http://tools.ietf.org/html/rfc3912) и занимает целых 4 страницы. В нескольких словах, все сводится к следующему: откройте TCP соединение на порт 43 к нужному whois серверу, пошлите запрос в определенном формате (который для конкретного whois сервера может быть каким угодно), закончите его "\r\n" и получите результат, формат которого для конкретного whois сервера также может быть каким угодно. Закрытие сервером соединения означает окончание результата. Все! Иными словами, каждый whois сервер определяет формат коммуникации по собственному усмотрению. Это уже не говоря о том, что абсолютно неочевидно, откуда взять нужный whois сервер для конкретного домена или IP адреса.

Я, конечно, наивно рассчитывал, что найду в Интернете море практических статей, в которых все это будет детально расписано, однако в результате не нашел ни одной. В основном, все сводилось лишь к пережевыванию скудной информации из RFC и названиям нескольких самых известных whois серверов. Хотя некоторую разрозненную информацию все-таки удалось найти.

В общем, пришлось смотреть код Unix-ового whois, а также jwhois и Ruby Whois — их принцип работы и будет рассмотрен далее.

Unix-овый whois


Итак, что собой представляет работа Unix-ового whois?

  • Если мы ищем домен, то определяем для него домен верхнего уровня (например, «ru») и посылаем запрос на whois сервер типа ru.whois-servers.net. Если полученный результат содержит строку вида «Whois Server: <название сервера>», то посылаем запрос также на этот новый сервер и результаты объединяем.
  • Если мы ищем IP адрес, то посылаем запрос на whois.arin.net (whois сервер, ответственный за Северную Америку). Если полученный результат упоминает один из известных нам региональных серверов (латиноамериканский, европейский, тихоокеанский и африканский), то посылаем запрос также на этот новый сервер и результаты объединяем.
  • Для большинства whois серверов запрос посылается в формате "<домен или IP адрес>\r\n". Для двух whois серверов (whois.denic.de и whois.dk-hostmaster.dk) запрос посылается в своем особом формате.
  • Пользователь имеет возможность сам указать whois сервер, к которому нужно обращаться.

Примечание: дополнительная функциональность, которая меня не интересовала (не связанная с доменами и IP адресами), здесь и далее опускается.

Как видим, все предельно просто. И, на самом деле, во многих случаях работает. Для многих доменов <домен верхнего уровня>.whois-servers.net является алиасом настоящего whois сервера (но для многих не является, и запрос, естественно, обламывается). Также whois.arin.net для многих IP адресов, за которые он сам не отвечает, тем не менее, знает правильный региональный сервер и корректно к нему отсылает (однако в ряде случаев все-таки не знает или отсылает к нему в произвольном формате, не обязательно с упоминанием названия самого whois сервера, на что рассчитывает программа). Большинство whois серверов действительно понимают формат вида "<домен или IP адрес>\r\n", однако исключения отнюдь не ограничиваются двумя серверами. Ну и, конечно же, ссылка на другой whois сервер совсем не обязательно будет строго в формате «Whois Server: <название сервера>».

Кроме того, часто результат получается не совсем такой, как мы ожидаем. Например:

$ whois google.com

GOOGLE.COM.ZZZZZZZZZZZZZZZZZZZZZZZZZZZ.LOVE.AND.TOLERANCE.THE-WONDERBOLTS.COM
GOOGLE.COM.ZZZZZZZZZZZZZZZZZZZZZZZZZZ.HAVENDATA.COM
GOOGLE.COM.ZZZZZZZZZZZZZ.GET.ONE.MILLION.DOLLARS.AT.WWW.UNIMUNDI.COM
GOOGLE.COM.ZZZZZ.GET.LAID.AT.WWW.SWINGINGCOMMUNITY.COM
GOOGLE.COM.ZOMBIED.AND.HACKED.BY.WWW.WEB-HACK.COM
...
GOOGLE.COM.AU
GOOGLE.COM.AR
GOOGLE.COM.ALL.THE.PEOPLE.WHO.SPAM.THE.WHOIS.ARE.SERIOUSLY.ANNOYING.SOMEPONY.COM
GOOGLE.COM.AFRICANBATS.ORG
GOOGLE.COM.9.THE-WONDERBOLTS.COM
GOOGLE.COM.1.THE-WONDERBOLTS.COM
GOOGLE.COM

To single out one record, look it up with "xxx", where xxx is one of the
of the records displayed above. If the records are the same, look them up
with "=xxx" to receive a full display for each record.

Оказывается, whois сервер знает про целый ряд доменов, похожих на «google.com» и не знает, какой из них выбрать.

Или еще пример:

$ whois 8.8.8.8

Level 3 Communications, Inc. LVLT-ORG-8-8 (NET-8-0-0-0-1) 8.0.0.0 - 8.255.255.255
Google Incorporated LVLT-GOOGL-1-8-8-8 (NET-8-8-8-0-1) 8.8.8.0 - 8.8.8.255

Как видим, IP адрес сначала был делегирован одной организации в рамках определенного диапазона, а позднее переделегирован другой организации в рамках уже меньшего поддиапазона. И whois сервер снова не знает, какая именно информация нас интересует.

Иными словами, программа полагается на самый общий принцип работы whois протокола и абсолютно не готова к каким-либо исключениям. Для «дискавери» доменных whois серверов используется whois-servers.net, что в настоящее время отнюдь не самый эффективный способ.

jwhois


Теперь посмотрим, как работает самая популярная whois утилита. В отличие от Unix-ового whois она не делает никакого «дискавери», а всецело полагается на километровый конфигурационный файл (около тысячи строк!), в котором захардкоджены whois серверы для всех известных доменов верхнего уровня, для ряда специфических доменов второго уровня (которые имеют собственные whois серверы) и даже для конкретных диапазонов IP адресов. Стоит ли говорить, что в современном быстро меняющемся мире этот конфигурационный файл будет требовать ежедневных обновлений и все равно вряд ли когда-нибудь будет на 100% актуальным? В шапке файла есть ссылка на репозиторий, с которого можно скачивать последнюю версию, и оказалась, что она датирована апрелем 2011-го года! За это время появилось множество новых whois серверов, а часть старых уже не работает. Уверен, что делегирован и целый ряд новых IP диапазонов — особенно для IPv6.

Кроме собственно названий whois серверов, в файле в специальном формате описаны все известные исключения для форматов запросов, а также формат ссылок на другие whois серверы (намного больше, чем в Unix-овом whois).

Также обнаружилась еще одна интересная функциональность. Для многих доменов верхнего уровня whois серверов на самом деле нет, или доступ к ним ограничен, однако whois информация доступна через веб на их сайте. Так вот, jwhois умеет посылать GET-ом или POST-ом нужный запрос, получать результат и потом выкусывать из HTML необходимую информацию. Адреса страниц, имена параметров формы и формат результата для каждого конкретного случая также захардкоджены в конфигурационном файле. Естественно, во многих случаях эти данные уже устарели, и «scraping» не работает. Некоторые сайты просто добавили в свои формы каптчу — возможно, как раз против подобных программ.

Возвращаясь к примерам выше, jwhois корректно выдает результат для «google.com», но с «8.8.8.8» он ничем не лучше Unix-ового whois.

В общем, как оказалось, jwhois не такая уж и хорошая программа, как думалось. Полагаться только на захардкодженный список серверов (даже если бы он регулярно обновлялся) явно не самая лучшая идея.

Ruby Whois


Фактически, Ruby Whois работает так же, как и jwhois, с той лишь разницей, что обновления к конфигурации выходят чуть ли не каждую неделю. Их changelog пестрит обновлениями типа «Update whois.nic.ms to the new response format», «whois.coza.net.za became whois.registry.net.za» или «Added .AX definition and parser». Кроме того, конфигурация представляет собой не один файл, а целое дерево файлов, где для каждого whois сервера педантично прописывается формат запроса и формат результата, а также образец ответа сервера для существующего и не существующего доменов (вероятно, для юнит тестов).

Если бы эта программа анализировала древнеегипетские папирусы, то, безусловно, имело бы смысл детально описать формат всех известных видов документов каждой из династий фараонов. Но вот делать это для whois серверов, которые меняются чуть ли не каждый день, наверно, не самое благодарное занятие. Как-то прямо жалко авторов проекта — поддерживать его явно задача не из легких.

На тот момент у меня уже были в наличии названия нескольких «хитрых» whois серверов (и понимание того, откуда их можно взять) — в конфигурации Ruby Whois никакой информации про них не было.

Я не имел возможности (да и особого желания) запускать Ruby Whois, поэтому не знаю, справился бы он с «8.8.8.8» или нет.

pwhois


Еще одна программа, которую мне посоветовали в комментариях к статье. Она написана на Perl и является оболочкой для модуля Net::Whois::Raw. И то, и другое можно скачать здесь: http://search.cpan.org/~despair/Net-Whois-Raw-2.43/. Последнее обновление датировано августом 2012 года.

Программа использует все тот же метод хардкода: есть огромный конфигурационный файл (на этот раз, на две тысячи строк), в котором зашиты названия whois серверов и все остальное.

Основной недостаток тот же самый: всего не захардкодить. Многих whois серверов в конфигурации нет, соответственно о целом ряде доменов получить информацию невозможно. Ну и с «8.8.8.8» программа также не справилась.

Что дальше?


К этому времени у меня уже были определенные идеи касательно того, как должен работать «правильный» whois, и я взялся за разработку начальной версии. Все дальнейшие «знания» были почерпнуты экспериментальным путем в результате множества разнообразных whois запросов и анализа их результатов. К счастью, многое из этого можно было автоматизировать.

Также хочется упомянуть сайт http://whois.domaintools.com — лучший whois веб сервис, который мне удалось найти. Естественно, я не имел возможности видеть его исходный код, однако во многих случаях сравнение его результатов с моими служило хорошей «наводкой». Не буду озвучивать предположения касательно того, как он работает, однако по факту он показывал намного лучшие результаты, чем вышеупомянутые программы (как я уже упоминал, о возможностях Ruby Whois я сужу только по коду).

Работу с whois я бы свел к решению трех принципиальных задач:

  1. Определение правильного whois сервера;
  2. Отправка правильного запроса на сервер;
  3. Анализ полученного результата.

Итак, приступим.

Как определить правильный whois сервер для домена?


Для начала несколько замечаний.

Whois информацию возможно получить не для всех доменов верхнего уровня. Некоторые страны whois информацию для своих доменов не предоставляют в принципе (например, КНДР). Кроме того, своих whois серверов не имеют и некоторые африканские страны (возможно, у них просто нет денег или всех программистов съели).

Для некоторых доменов верхнего уровня whois информацию возможно получить только на сайте регистратора. В некоторых случаях для этого необходимо ввести каптчу, в остальных это можно реализовать программно. Тем не менее, я полностью отказался от идеи «scraping-а». Его корректная работа требует хардкода большого количества информации (адрес страницы, имена полей формы, формат полученной HTML страницы и т.п.), чего я всячески хотел избежать. Кроме того, эта информация требует постоянного обновления, так как она может меняться даже чаще, чем имена whois серверов.

Ну и, самое главное, если whois информация будет отображаться посетителям сайта, то можно просто предоставить ссылку на сайт регистратора и дать возможность пользователю заполнить форму самому. В большинстве случаев, whois форма представлена или на заглавной странице, или на страницу с ней можно перейти в один клик. Это намного более надежный способ, чем полагаться на «scraping», который в любом случае не сможет справиться с каптчей. Где взять ссылку на сайт регистратора будет рассказано ниже.

Также некоторые whois серверы могут банить IP адреса пользователей, которые посылают слишком много запросов.

Итак, где же взять название правильного whois сервера?

Взять его можно из нескольких источников:

1. Есть такой замечательный whois сервер whois.iana.org, принадлежащий IANA (http://en.wikipedia.org/wiki/Internet_Assigned_Numbers_Authority). Он содержит самую свежую информацию обо всех доменах верхнего уровня. Например (здесь и далее все примеры с использованием Unix-ового whois):

$ whois -h whois.iana.org ru

domain:       RU

organisation: Coordination Center for TLD RU
address:      8, Zoologicheskaya str.
address:      Moscow  123242
address:      Russian Federation

contact:      administrative
name:         .RU domain Administrative group
organisation: Coordination Center for TLD RU
address:      8, Zoologicheskaya str.
address:      Moscow  123242
address:      Russian Federation
phone:        +7 499 254 88 94
fax-no:       +7 499 254 89 63
e-mail:       ru-adm@cctld.ru

contact:      technical
name:         Technical Center of Internet
organisation: Technical Center of Internet
address:      8, Zoologicheskaya str.
address:      Moscow  123242
address:      Russian Federation
phone:        +7 495 737 92 95
fax-no:       +7 495 737 06 84
e-mail:       ru-tech@tcinet.ru

nserver:      A.DNS.RIPN.NET 193.232.128.6 2001:678:17:0:193:232:128:6
nserver:      B.DNS.RIPN.NET 194.85.252.62 2001:678:16:0:194:85:252:62
nserver:      D.DNS.RIPN.NET 194.190.124.17 2001:678:18:0:194:190:124:17
nserver:      E.DNS.RIPN.NET 193.232.142.17 2001:678:15:0:193:232:142:17
nserver:      F.DNS.RIPN.NET 193.232.156.17 2001:678:14:0:193:232:156:17
ds-rdata:     14072 8 2 DFFBFE59FBBD3289D0C3819F05F94610A1E03B556D64540A2CC5F8C4158A00E7

whois:        whois.tcinet.ru

status:       ACTIVE
remarks:      Registration information: <span>http</span>://www.cctld.ru/en

created:      1994-04-07
changed:      2012-12-21
source:       IANA

Обратим внимание на следующие строки:

whois:        whois.tcinet.ru
remarks:      Registration information: <span>http</span>://www.cctld.ru/en

Это собственно и есть whois сервер и сайт регистратора! Для большинства доменов верхнего уровня IANA возвращает вполне актуальное название whois сервера. Я специально проверял изменения в Ruby Whois за несколько последних месяцев, и все они были взяты именно с whois.iana.org.

В моем модуле я кэширую результат с IANA на 24 часа (на больший промежуток времени особого смысла нет, да и данные могут поменяться).

Несмотря на все сказанное выше, для некоторых доменов верхнего уровня IANA информацию о whois серверах почему-то не предоставляет. Возможно, регистраторы некоторых стран специально их не афишируют. Но не страшно, у нас есть и другие способы.

2. Если присмотреться, для очень многих доменов верхнего уровня названия их whois серверов выглядят или как whois.nic.<домен верхнего уровня> (более распространено), или как whois.<домен верхнего уровня> (менее распространено). Например:

  • whois.nic.fr
  • whois.nic.it
  • whois.biz

Более того, если даже IANA указывает какой-то другой whois сервер (например, whois.registro.br), во многих случаях алиас, построенный по этой схеме (для нашего примера, whois.nic.br) все равно будет работать.

Таким образом, если IANA нам ничего не вернула, для любого домена верхнего уровня мы легко получаем названия двух потенциальных whois серверов:

  • whois.nic.<домен верхнего уровня>
  • whois.<домен верхнего уровня>

Практика показала, что этот способ выявляет действующие whois серверы для доброго десятка доменов верхнего уровня, информацию о которых IANA не возвращает.

Довольно часто, если мы ищем домен третьего уровня (например, russia.edu.ru), то whois сервер, ответственный за домен верхнего уровня, нужной информации не содержит. Например:

$ whois -h whois.tcinet.ru russia.edu.ru

No entries found for the selected source(s).

Last updated on 2013.01.04 01:41:36 MSK

Это происходит из-за того, что за многие домены второго уровня (в нашем примере, edu.ru) ответственна совершенно другая организация, которая имеет свой собственный whois сервер.

В подобных случаях jwhois и Ruby Whois педантично хардкодят название whois сервера для каждого известного им домена второго уровня с собственным сервером. Естественно, уследить за всеми доменами второго уровня абсолютно нереально, тем более что никакого централизованного источника подобной информации нет.

Но опять таки, если присмотреться, большинство таких whois серверов имеют вид whois.<домен второго уровня> (более распространено) или whois.nic.<домен второго уровня> (менее распространено). Например:

  • whois.za.net
  • whois.eu.org
  • whois.nic.priv.at

Как же быть с теми whois серверами, которые называются по-другому? Например, в конфигурации Ruby Whois можно увидеть:

  • ".ae.org", «whois.centralnic.com»
  • ".edu.ru", «whois.informika.ru»

С самого начала я был морально готов к необходимости захардкодить несколько whois серверов и у себя в коде, однако, перебрав абсолютно все подобные серверы, упомянутые в конфигурациях jwhois и Ruby Whois, оказалось, что все они на самом деле доступны также и за алиасами вида whois.<домен второго уровня> или whois.nic.<домен второго уровня>! Для нашего примера:

  • whois.ae.org
  • whois.edu.ru

В конечном итоге, мне не пришлось хардкодить ни одного whois сервера вообще (естественно, кроме whois.iana.org).

3. Кроме описанного выше, название whois сервера еще может выглядеть как whois.<сервер сайта регистратора>. Например:

$ whois -h whois.iana.org br

whois:        whois.registro.br
remarks:      Registration information: <span>http</span>://registro.br/

В некоторых случаях, когда IANA возвращает только сайт регистратора без whois сервера, название, построенное по этой схеме, оказывается верным.

4. Как уже упоминалось, whois-servers.net содержит алиасы на whois серверы лишь для достаточно ограниченного числа доменов верхнего уровня, но в отдельных случаях, когда все вышеперечисленное не помогает, алиас вида <домен верхнего уровня>.whois-servers.net оказывается вполне работоспособным (например, для «ps»).

Таким образом, для каждого домена, который мы хотим найти, у нас будет целый список потенциальных whois серверов с большей или меньшей вероятностью работоспособности. Например, для russia.edu.ru у нас будет:

  • whois.tcinet.ru (результат IANA для «ru»)
  • whois.edu.ru << нужный нам whois сервер
  • whois.nic.edu.ru
  • whois.nic.ru
  • whois.ru
  • whois.cctld.ru («вымышленный» сервер на основе адреса сайта регистратора)
  • ru.whois-servers.net (по факту, работающий алиас для whois.tcinet.ru)

Какой же из них выбрать? На самом деле, выбирать ничего не нужно, так как не составляет труда опросить все найденные whois серверы в порядке приоритета. Естественно, если результат найден, то опрашивать следующие серверы уже не нужно. Кроме того, если какого-то из этих whois серверов на самом деле не существует (а среди «вымышленных» таких будет, естественно, большинство), то эту информацию легко можно закэшировать.

Про кэш хотелось бы рассказать отдельно.

Whois сервер от IANA и whois серверы, найденные по ссылкам (об этом чуть ниже), по умолчанию считаются «активными». Когда активный сервер не отвечает (имеется в виду, сервер не доступен в принципе, а не просто не нашел нужной информации для какого-то конкретного домена и вернул ошибку), его статус меняется на «временно недоступный». Когда не отвечает временно недоступный сервер, он блокируется сначала на одну минуту, затем на две, на четыре, на восемь и т.д., пока этот период не станет большим, чем две недели. В этом случае статус сервера меняется на «неактивный», и он блокируется ровно на две недели. Если неактивный сервер не отвечает и через две недели, он остается неактивным и снова блокируется на две недели. Если временно недоступный или неактивный сервер вдруг ответил, его статус меняется на активный. Все «вымышленные» whois серверы и серверы вида <домен верхнего уровня>.whois-servers.net по умолчанию считаются неактивными. То есть если они не отвечают, то немедленно блокируются на две недели.

Такая схема позволяет отфильтровать все несуществующие whois серверы и в то же время не дает надолго заблокировать серверы, которые по тем или иным причинам стали временно недоступны.

Стоит отметить, что когда мы в следующий раз будем искать домен в зоне edu.ru, то активными будут считаться сразу два whois сервера:

  • whois.tcinet.ru
  • whois.edu.ru

Однако тот факт, что whois.edu.ru активен, говорит о том, что первый whois сервер, скорее всего, нужной нам информации не содержит (иначе очередь до whois.edu.ru бы не дошла, и активным он бы не стал). Поэтому будет целесообразно поставить его первым по приоритету и опрашивать whois серверы в таком порядке:

  • whois.edu.ru << нужный нам whois сервер
  • whois.tcinet.ru
  • whois.nic.edu.ru
  • whois.nic.ru
  • whois.ru
  • whois.cctld.ru
  • ru.whois-servers.net

Если же мы будем, например, искать daily.lviv.ua (также домен третьего уровня), то результат для него, скорее всего, вернет уже самый первый whois сервер, который нам вернула IANA (whois.ua). Поэтому сервер whois.lviv.ua активным так и не станет (возможно, его и не существует — я не проверял), и в следующий раз домен в зоне lviv.ua мы снова станем искать на whois.ua.

Иными словами, через некоторое время запросы к несуществующим или неверным whois серверам будут сведены к минимуму.

Ссылки на дополнительные whois серверы


Часто результат, который выдал whois сервер, бывает неполным, однако в нем содержится ссылка на другой whois сервер, который содержит более полную информацию. Например:

$ whois -h whois.verisign-grs.com 'domain google.com'

Domain Name: GOOGLE.COM
Registrar: MARKMONITOR INC.
Whois Server: whois.markmonitor.com
Referral URL: <span>http</span>://www.markmonitor.com
Name Server: NS1.GOOGLE.COM
Name Server: NS2.GOOGLE.COM
Name Server: NS3.GOOGLE.COM
Name Server: NS4.GOOGLE.COM
Status: clientDeleteProhibited
Status: clientTransferProhibited
Status: clientUpdateProhibited
Status: serverDeleteProhibited
Status: serverTransferProhibited
Status: serverUpdateProhibited
Updated Date: 20-jul-2011
Creation Date: 15-sep-1997
Expiration Date: 14-sep-2020

Как видим, информации не так уж и много, но есть ссылка на другой whois сервер:

Whois Server: whois.markmonitor.com

Если теперь обратиться к нему, то получим:

$ whois -h whois.markmonitor.com google.com

Registrant:
    Dns Admin
    Google Inc.
    Please contact contact-admin@google.com 1600 Amphitheatre Parkway
     Mountain View CA 94043
    US
    dns-admin@google.com +1.6502530000 Fax: +1.6506188571

Domain Name: google.com

    Registrar Name: Markmonitor.com
    Registrar Whois: whois.markmonitor.com
    Registrar Homepage: <span>http</span>://www.markmonitor.com

Administrative Contact:
    DNS Admin
    Google Inc.
    1600 Amphitheatre Parkway
     Mountain View CA 94043
    US
    dns-admin@google.com +1.6506234000 Fax: +1.6506188571
Technical Contact, Zone Contact:
    DNS Admin
    Google Inc.
    2400 E. Bayshore Pkwy
     Mountain View CA 94043
    US
    dns-admin@google.com +1.6503300100 Fax: +1.6506181499

Created on..............: 1997-09-15.
Expires on..............: 2020-09-13.
Record last updated on..: 2012-01-29.

Domain servers in listed order:

ns2.google.com
ns4.google.com
ns3.google.com
ns1.google.com

Таким образом, чтобы получить максимально полную информацию, мы должны искать в результате ссылки на другие whois серверы. Чаще всего они выглядят как «whois server: <название>», однако нередко ссылка бывает и в совершенно непредсказуемом месте (например, в середине текста).

Мой модуль ищет ссылки достаточно агрессивно, принимая за whois сервер любую строку, которая выглядит как хостнейм и содержит слово «whois» (как мы уже выяснили, «лишние» whois серверы — не проблема). Если же мы нашли ссылку в формате «whois server: <название>», то название сервера уже не обязательно должно содержать слово «whois» (на практике, это может быть даже не хостнейм, а просто IP адрес). Все найденные whois серверы по умолчанию считается активными.

Правда здесь есть один подводный камень. Результат может содержать название собственного whois сервера, и если так вышло, что мы обратились к нему по какому-то алиасу, оно будет выглядеть как ссылка на другой whois сервер. Например:

$ whois -h whois.jp newsmap.jp

[ JPRS database provides information on network administration. Its use is    ]
[ restricted to network administration purposes. For further information,     ]
[ use 'whois -h whois.jprs.jp help'. To suppress Japanese output, add'/e'     ]
[ at the end of command, e.g. 'whois -h whois.jprs.jp xxx/e'.                 ]
...

Здесь упоминается whois.jprs.jp, алиасом которого, на самом деле, и является whois.jp. То есть если мы теперь пошлем запрос на whois.jprs.jp, то получим такой же самый результат. Кроме этого, некоторые whois серверы могут работать как прокси и возвращать результаты с какого-то другого whois сервера — естественно, упомянув его имя в результате.

Иными словами, нам нужно каким-то образом выявлять подобные ситуации: во-первых, чтобы не выдавать пользователю два одинаковых результата, а во-вторых, чтобы не тратить время на ненужные запросы.

На первый взгляд, решение элементарное: нужно просто сравнить два результата, и если они одинаковые, второй откинуть (запомнив, что переход между этими двумя whois серверами в дальнейшем делать не нужно). Однако не все так просто. Часто результат содержит точное время, когда был сделан запрос — соответственно, два запроса, сделанные подряд, будут отличаться значением времени. Если первый whois сервер работает как прокси, то он, как правило, будет содержать еще какой-то дополнительный текст. И самое интересное: некоторые whois серверы умудряются от запроса к запросу выдавать данные в разном порядке! То есть результат запросов абсолютно одинаковый, но несколько строк поменяны местами.

Поэтому перед сравнением я заменяю все последовательности цифр на «X», удаляю все пустые строки и строки, которые начинаются с "#" или "%" (комментарии), и, на всякий случай, заменяю множественные пробелы на один и делаю «trim» всем строкам. После этого я сравниваю два множества строк (используя тип set в Python), и если второе является подмножеством первого, значит это дубликат.

С IDN доменами (типа «правительство.рф»), к счастью, никаких проблем нет. Переводим «правительство.рф» в «xn--80aealotwbjpid2k.xn--p1ai» и ищем на whois.iana.org домен верхнего уровня «xn--p1ai»:

whois:        whois.tcinet.ru

Как определить правильный whois сервер для IP адреса?


С IP адресами ситуация несколько другая. Есть пять основных региональных whois серверов:

  • whois.arin.net (ARIN, Северная Америка)
  • whois.apnic.net (APNIC, Азия и Тихоокеанский регион)
  • whois.ripe.net (RIPE, Европа и Ближний Восток)
  • whois.afrinic.net (AfriNIC, Африка)
  • whois.lacnic.net (LACNIC, Латинская Америка)

Теоретически, информация про любой IP адрес (как IPv4, так и IPv6) должна быть на одном из них. Если же какой-то диапазон IP адресов зарезервирован или ни одному из региональных серверов еще не выделен (это особенно актуально для IPv6), то об этом должна знать IANA. Например:

$ whois -h whois.iana.org 12af::

inet6num:     1000:0:0:0:0:0:0:0/4
descr:        Reserved by IETF

remarks:      <span>http</span>://tools.ietf.org/html/rfc4291

source:       IANA

Если спросить у какого-нибудь регионального сервера про IP адрес, за который он не отвечает, то с определенной вероятностью он сможет указать на нужный сервер, однако далеко не всегда. Поэтому я по очереди опрашиваю все пять региональных whois серверов, а также whois.iana.org, пока кто-то из них не вернет результат или не укажет на нужный whois сервер.

Первый запрос я всегда посылаю на ARIN, так как он отвечает за наибольшее число IP адресов (да и пользователей нашего продукта чаще всего будут интересовать именно IP адреса из Северной Америки). Кроме того, для остальных IP адресов ARIN почти всегда может указать на нужный региональный whois сервер. В большинстве случаев IANA также может указать на правильный сервер, однако в таком случае пришлось бы каждый раз делать минимум по два запроса.

Стоит отметить, что AfriNIC — относительно новый whois сервер. До этого все африканские IP адреса были распределены между ARIN, RIPE и APNIC. Поэтому другие региональные whois серверы часто думают, что эти IP адреса до сих пор им делегированы. Например:

$ whois -h whois.iana.org 213.154.64.0

refer:        whois.ripe.net

inetnum:      213.0.0.0 - 213.255.255.255
organisation: RIPE NCC
status:       ALLOCATED

whois:        whois.ripe.net

changed:      1993-10
source:       IANA

ARIN также скажет, что за 213.154.64.0 отвечает RIPE. Если же обратиться к RIPE, то он ответит, что за диапазон 213.154.64.0 — 213.154.95.255 уже отвечает AfriNIC.

Также стоит помнить про две вещи. whois.lacnic.net работает как прокси, и если про какой-то IP адрес информации не имеет, то отправляет запрос на остальные четыре сервера и, в конечном итоге, возвращает нужный результат. Может показаться, что в таком случае все запросы нужно просто посылать на LACNIC. На самом деле, делать это абсолютно не стоит: два из пяти региональных whois серверов (RIPE и AfriNIC) достаточно агрессивно банят IP адреса пользователей, которые посылают слишком много запросов. Не уверен на счет точных цифр, но 10000 запросов в сутки, думаю, будет достаточно, чтобы оказаться заблокированным AfriNIC. Когда LACNIC посылает запросы на другие региональные серверы, он, естественно, указывает IP пользователя, от имени которого делается запрос. Иными словами, если даже какой-то IP адрес не принадлежит AfriNIC, есть большая вероятность, что LACNIC отправит запрос и на него, тем самым, увеличив счетчик. Поэтому на LACNIC я отправляю запросы в последнюю очередь.

Ссылки на другие whois серверы ищутся так же само, как и для доменов. Правда есть несколько нюансов. ARIN всегда показывает ссылки на дополнительные whois серверы в виде «ReferralServer: <название>». Если же он за IP адрес не отвечает, но знает, на каком из региональных серверов его нужно искать, то указывать полное название whois сервера он, скорее всего, не будет, а просто ограничится аббревиатурой типа LACNIC или RIPE.

Как правильно отправить запрос?


Итак, с горем пополам, мы наконец-то нашли нужный whois сервер. Как теперь отправить на него запрос?

В подавляющем большинстве случаев нужно просто открыть соединение на порт 43, отправить строку вида "<домен или IP адрес>\r\n" и прочитать данные, которые вернет сервер. Однако, как всегда, есть исключения (тут без хардкода, к сожалению, уже не обойтись).

Не смотря на то, что RFC 3912 явно предписывает посылать "\r\n", есть как минимум один whois сервер, который требует "\n" и с "\r\n" работать отказывается! Это whois сервер Мальты whois.nic.org.mt.

Кроме того, следующие whois серверы требуют особый синтаксис:

  • whois.arin.org: «n + <IP адрес>\r\n»
  • whois.denic.de: "-C UTF-8 -T dn,ace <домен>\r\n"
  • whois.dk-hostmaster.dk: "--show-handles <домен>\r\n"
  • whois.nic.name: «domain = <домен>\r\n»
  • whois серверы, принадлежащие VeriSign: «domain <домен>\r\n»

Основной VeriSign сервер — whois.verisign-grs.com. Как же определить остальные? Если в ответ на запрос "<домен>\r\n" результат содержит строки `To single out one record, look it up with «xxx»` и `look them up with "=xxx" to receive a full display`, то перед нами VeriSign сервер. В этом случае запрос должен быть повторен как «domain <домен>\r\n», а сам whois сервер занесен в список VeriSign серверов.

К whois.arin.org можно посылать запросы и в обычном виде "<IP адрес>\r\n", но тогда есть риск получить вместо полного результата вот это:

Level 3 Communications, Inc. LVLT-ORG-8-8 (NET-8-0-0-0-1) 8.0.0.0 - 8.255.255.255
Google Incorporated LVLT-GOOGL-1-8-8-8 (NET-8-8-8-0-1) 8.8.8.0 - 8.8.8.255

С whois.nic.name ситуация вообще интересная. Например:

$ whois -h whois.nic.name 'domain = d-n-s.name'

Domain Name ID: 1102376DOMAIN-NAME
Domain Name: D-N-S.NAME
Sponsoring Registrar ID: 44REGISTRAR-NAME
Sponsoring Registrar: GoDaddy.com, LLC
Domain Status: clientDeleteProhibited
Domain Status: clientRenewProhibited
Domain Status: clientTransferProhibited
Domain Status: clientUpdateProhibited
Registrant ID: 1337930CONTACT-NAME
Admin ID: 1337932CONTACT-NAME
Tech ID: 1337931CONTACT-NAME
Billing ID: 1337933CONTACT-NAME
Name Server ID: 1017197HOST-NAME
Name Server: NS3.AFRAID.ORG
Name Server ID: 1017198HOST-NAME
Name Server: NS4.AFRAID.ORG
Name Server ID: 2249HOST-NAME
Name Server: NS1.AFRAID.ORG
Name Server ID: 2250HOST-NAME
Name Server: NS2.AFRAID.ORG
Created On: 2004-09-04T19:14:53Z
Expires On: 2013-09-04T19:14:53Z
Updated On: 2012-09-03T09:10:57Z

Информации не так уж и много (хотя и лучше, чем ничего). Однако обратите внимание на строку:

Sponsoring Registrar ID: 44REGISTRAR-NAME

Можно отправить такой запрос:

$ whois -h whois.nic.name 'registrar = 44REGISTRAR-NAME'

Registrar ID: 44REGISTRAR-NAME
Registrar Name: GoDaddy.com, LLC
Registrar URL: <span>www</span>.godaddy.com
Registrar Status: ok
Registrar Address: 14455 North Hayden Rd, Suite 226
Registrar City: Scottsdale
Registrar State/Province: Arizona
Registrar Country: UNITED STATES
Registrar Postal Code: 85260
Registrar Phone Number: +1.4805058800
Registrar Fax Number: +1.4805058865
Registrar E-mail: registrar_routine@godaddy.com
Admin ID: 1269602CONTACT-NAME
Admin Organization: Go Daddy Software, Inc.
Admin Name: Tim Ruiz
Admin Address: 14455 N Hayden Suite 226, ,
Admin City: Scottsdale
Admin State/Province: AZ
Admin Country: UNITED STATES
Admin Postal Code: 85260
Admin Phone Number: +1.4805058800
Admin Fax Number: +1.4805058865
Admin Email: registrar_routine@godaddy.com
...

Самого whois сервера регистратора он не показал, но показал адрес сайта:

Registrar URL: <span>www</span>.godaddy.com

Как мы уже знаем, в большинстве случаев whois сервер находится где-то неподалеку. Пробуем:

$ whois -h whois.godaddy.com d-n-s.name

Registered through: GoDaddy.com, LLC (<span>http</span>://www.godaddy.com)
Domain Name: D-N-S.NAME
   Created on: 04-Sep-04
   Expires on: 04-Sep-13
   Last Updated on: 03-Sep-12

Registrant:
Mike Taylor
9 Ludford Grove
Sale,  M33 4DP
United Kingdom

Administrative Contact:
   Taylor, Mike  admin@crazyemail.net
   9 Ludford Grove
   Sale,  M33 4DP
   United Kingdom
   +7.745095404

Technical Contact:
   Taylor, Mike  admin@crazyemail.net
   9 Ludford Grove
   Sale,  M33 4DP
   United Kingdom
   +7.745095404
...

Это, конечно, работает не для всех доменов в зоне «name», но для многих.

Также рекомендую почитать про дополнительный синтаксис региональных whois серверов (детально описан на их сайтах). Я его у себя в модуле не использую, но кому-то может пригодиться. Например, параметр "-B" позволяет whois.ripe.net выводить поля «notify», «changed» и «e-mail», которые по умолчанию не показываются:

$ telnet whois.ripe.net 43
Trying 193.0.6.135...
Connected to whois.ripe.net.
Escape character is '^]'.
% This is the RIPE Database query service.
% The objects are in RPSL format.
%
% The RIPE Database is subject to Terms and Conditions.
% See http://www.ripe.net/db/support/db-terms-conditions.pdf

-B 213.180.204.0                                       <<<<< обратите внимание на "-B"
% Information related to '213.180.204.0 - 213.180.204.255'

inetnum:        213.180.204.0 - 213.180.204.255
netname:        YANDEX-213-180-204
descr:          Yandex enterprise network
country:        RU
admin-c:        YNDX1-RIPE
tech-c:         YNDX1-RIPE
remarks:        INFRA-AW
status:         ASSIGNED PA
mnt-by:         YANDEX-MNT
changed:        artem@yandex-team.ru 20091116          <<<<< дополнительное поле
source:         RIPE
...

В этом примере я использую telnet, так как через Unix-овый whois или другую утилиту передать специальные параметры не удается.

Еще один нюанс связан со считыванием результатов с whois сервера. Первоначально у меня был приблизительно такой код:

obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
    ...

    res = ''
    while True:
        buf = obj.recv(4096)
        if buf:
            res += buf
        else:
            break

    return res
except Exception:
    return ''

Иными словами, читаем данные, пока они есть. Если происходит ошибка, возвращаем пустой результат.

Оказалось, что это работает не всегда (хотя и, наверно, в 99% случаев). Есть whois серверы, которые после отправки данных закрывают соединение таким образом, что на клиенте всегда происходит ошибка (хотя все данные перед этим были успешно прочитаны).

Поэтому код был изменен на:

obj = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
...

res = ''
while True:
    try:
        buf = obj.recv(4096)
    except socket.error:
        break

    if buf:
        res += buf
    else:
        break

return res

Наконец, оказалось, что кроме протокола whois, существует еще один, который называется rwhois. О нем речь пойдет ниже.

Referral Whois


Что такое rwhois («Referral Whois») можно прочитать здесь: http://en.wikipedia.org/wiki/Whois#Referral_Whois. В нескольких словах, это протокол, который должен исправить все недостатки стандартного whois и, в конце концов, полностью его заменить. По умолчанию протокол использует порт 4321. Не смотря на то, что rwhois был разработан еще в 1994-м году, широкого распространения он до сих пор так и не получил.

Впервые ссылки на rwhois серверы я увидел в результатах, которые мне возвращал ARIN (хотя подавляющее большинство ссылок по-прежнему вели на обыкновенные whois серверы). Обычно, уже само их название содержало слово «rwhois». Кроме того, в большинстве случаев ARIN явно указывал, какой протокол надо использовать: «whois://» или «rwhois://». Первый же rwhois сервер, к которому я попробовал отправить запрос, показал, что простым "<IP адрес>\r\n" тут не обойтись. Пришлось открывать спецификацию.

Referral Whois описан в RFC 2167 (http://tools.ietf.org/html/rfc2167). Это огромный нечитаемый документ на 170КБ. Проглядев его по диагонали, я сразу же вспомнил слова Joel-а Spolsky: «если кто-то дал вам спецификацию для какой-то новой технологии, и вы не можете ее понять, сильно не расстраивайтесь. Никто другой ее тоже не поймет, поэтому это вряд ли имеет значение».

Каких-либо вменяемых примеров в документе не содержалось, Google мне также ничем не смог помочь. Поэтому, повозившись с telnet-ом и окончательно зайдя в тупик, я решил, что это не тот случай, когда нужно изобретать велосипед. На http://projects.arin.net/rwhois/ лежал написанный на C клиент с вполне подходящей GPL v2 лицензией. Свыше 100КБ кода и поддержка rwhois версий 1.0 и 1.5 — самому такое не написать. С теми rwhois серверами, с которыми я зашел в тупик, он блестяще справился. Упомянутый выше jwhois также имел встроенную поддержку rwhois, но вряд ли она была настолько же совершенной (по крайней мене, поддержки разных версий сервера там не было).

В общем, если мой модуль находил rwhois сервер, то просто вызывал через subpocess сторонний rwhois клиент, который брал на себя всю коммуникацию с сервером и возвращал готовый результат.

Радость длилась недолго: оказалось, что с большинством реальных rwhois серверов клиент не работает. Никаких видимых причин для этого не было, однако клиент упрямо возвращал «error querying rwhois server». Google, естественно, ничем помочь не смог. Самое обидное, что jwhois с этими серверами прекрасно справлялся.

Провозившись довольно долго, я, наконец, обнаружил удивительную вещь: все эти «rwhois» серверы на самом деле работают по протоколу обычного whois! Иными словами, мой навороченный клиент соединяется с сервером и думает «интересно, он работает как rwhois версии 1.0 или rwhois версии 1.5?» А сервер на самом деле ничего кроме тупого "<IP адрес>\r\n" не понимает! Такой наглости клиент, естественно, не ожидает и выдает ошибку.

Подозреваю, что все эти серверы банально «не осилили» правильно реализовать rwhois протокол и, в конце концов, вернулись к обычному whois, при этом продолжая формально идентифицировать себя как rwhois сервер. Так что Joel Spolsky был абсолютно прав.

На самом деле, кое-что от rwhois у этих серверов все-таки осталось: строго структурированный формат возвращаемых данных. Все разбито на секции, никаких лишних пробелов, имя/значение разделены двоеточием. Rwhois клиент потом трансформировал это в красивую таблицу с табуляцией. Реализовать то же самое в моем модуле заняло всего несколько строк кода.

Теперь, если rwhois клиент для какого-то rwhois сервера выдает ошибку, модуль пробует повторно обратиться к нему как к обычному whois серверу. Если это ему удается, сервер навсегда заносится в список обычных whois серверов.

Как обработать полученный результат?


Самое сложное — это отличить валидный результат от сообщения об ошибке. Задача абсолютно нетривиальная: формат сообщения об ошибке для конкретного whois сервера может быть каким угодно. Алгоритм был придуман и усовершенствован экспериментальным путем. У меня уже был достаточно большой набор валидных результатов от различных whois серверов, получить сообщения об ошибке также не составило труда: достаточно было отправить к каждому известному серверу запрос на несуществующий или некорректный домен или IP адрес (это легко автоматизировалось).

В конце концов, я пришел к следующему:

Если результат получен для IP адреса с одного из пяти региональных whois серверов, то нижеследующие строки точно свидетельствует об ошибке:

  • «0.0.0.0 — 255.255.255.255» или «0::/0» (для всех)
  • «network range is not allocated to apnic» (для APNIC)
  • «address block not managed by the ripe» или «does not operate any networks using» (для RIPE)
  • «afrinic whois server» или «ripe database query service» (для LACNIC)
  • «whois.arin.net» или «allocated to arin», при условии, что упоминается другой региональный сервер, чем к которому был запрос

Если проверка прошла успешно, то переходим к стандартной проверке (для всех результатов):

  1. Удаляем все, что выглядит как «http(s)://» или как время (например, «12:45:51»). Иными словами, удаляем любые двоеточия, которые заведомо не представляют для нас интереса.
  2. Удаляем все строки, которые начинаются с "#" или "%" (комментарии).
  3. Если домен или IP адрес, который мы ищем, в результате не упоминается, значит, результат невалидный. Исключение: результат с сервера whois.eu.
  4. Считаем количество «полезных» строк. Если строка содержит двоеточие ИЛИ содержит табуляцию в середине строки ИЛИ содержит отступ вначале строки и при этом меньше 5 слов, то она считается «полезной».
  5. Если у нас вышло больше 3 «полезных» строк, то результат считается валидным.
  6. Если «полезных» строк вышло меньше, чем нужно, но всех строк больше десяти (не считая тех, которые мы удалили вначале), и при этом результат содержит как самостоятельные слова строки «name» и «address», то результат также считается валидным.
  7. Иначе результат невалидный.

В общем, танцы с бубном. Уверен, что бывают ситуации, которые эта проверка не покрывает, но в подавляющем числе случаев должно работать.

Если ARIN вернул нам несколько результатов (например, для 8.8.8.8), то нужно выбрать из них нужный:

$ whois -h whois.arin.net 'n + 8.8.8.8'

# start

NetRange:       8.0.0.0 - 8.255.255.255
CIDR:           8.0.0.0/8
OriginAS:
NetName:        LVLT-ORG-8-8
NetHandle:      NET-8-0-0-0-1
Parent:
NetType:        Direct Allocation
RegDate:        1992-12-01
Updated:        2012-02-24
Ref:            <span>http</span>://whois.arin.net/rest/net/NET-8-0-0-0-1
...

# end


# start

NetRange:       8.8.8.0 - 8.8.8.255
CIDR:           8.8.8.0/24
OriginAS:
NetName:        LVLT-GOOGL-1-8-8-8
NetHandle:      NET-8-8-8-0-1
Parent:         NET-8-0-0-0-1
NetType:        Reassigned
RegDate:        2009-09-21
Updated:        2009-09-21
Ref:            <span>http</span>://whois.arin.net/rest/net/NET-8-8-8-0-1
...

# end

Разбиваем результат по #end и #start и выбираем ту часть, которая соответствует наименьшему диапазону (в нашем случае 8.8.8.0 — 8.8.8.255).

Ну и несколько последних штрихов:

1. Удаляем из результата все строки, которые содержат наш собственный IP адрес (многие whois серверы включают его в результат).

2. Удаляем все HTML тэги (некоторые whois серверы любят вставлять их, где не надо).

3. Исправляем кодировку для японских и корейских whois серверов:

  • Если название whois сервера оканчивается на ".jp", то пробуем интерпретировать результат как «iso-2022-jp» (если произошла ошибка, не страшно).
  • Если название whois сервера оканчивается на ".kr" или «krnic.net», то пробуем интерпретировать результат как «euc-kr» (если произошла ошибка, не страшно).
  • Пробуем интерпретировать результат как «utf8». Если произошла ошибка, интерпретируем его как «iso-8859-1».

Примечание: японские и корейские whois серверы поддерживают специальный синтаксис, который позволяет выводить только английский текст (без иероглифов), но я этого специально не делаю (в отличие от jwhois и http://whois.domaintools.com). Если человек ищет информацию про японский (корейский) домен, то с высокой вероятностью он знает японский (корейский) язык, и эта дополнительная информация будет его полезной. Всем остальным иероглифы тоже никак не навредят.

Вот как бы и все. Если вы прочитали всю статью, следующий раздел можете смело пропускать.

Короткие итоги


Работа whois протокола описана в RFC 3912 (http://tools.ietf.org/html/rfc3912), содержащем минимум информации и оставляющем whois серверам широкое поле для «самодеятельности». Фактически, все сводится к следующему: открыть TCP соединение на порт 43 к нужному whois серверу, послать запрос в определенном формате, закончить его "\r\n" и получить результат также в определенном формате. Конкретные детали каждый whois сервер определяет по собственному усмотрению. Также не всегда очевидно, откуда взять нужный whois сервер для конкретного домена или IP адреса.

Для получения whois информации существует стандартная Unix-овая утилита «whois», однако она использует очень простой и достаточно устаревший алгоритм, который во многих ситуациях не работает. Среди альтернатив можно выделить jwhois и Ruby Whois — обе программы используют принцип хардкода, работая на основе километровых конфигурационных файлов, которые содержат все известные whois серверы и описание особенностей работы с каждым из них. Такой подход также не всегда работает, кроме того, требует постоянного обновления конфигурации (стоит отметить, что конфигурация jwhois не обновлялась с 2011-го года).

Как показала практика, вполне реально написать свою собственную whois библиотеку, которая будет иметь минимум хардкода, но при этом сможет эффективно определять нужные whois серверы и коммуницировать с ними.

В целом, работа с whois сводится к решению трех принципиальных задач:

  1. Определение правильного whois сервера;
  2. Отправка правильного запроса на сервер;
  3. Анализ полученного результата.

Сразу же стоит отметить, что существует ряд доменов, whois информация о которых не доступна в принципе или же доступна только на сайте регистратора. Найти правильный whois сервер для остальных доменов можно при помощи нижеследующих способов:

  1. Про большинство whois серверов знает whois.iana.org. Достаточно отправить запрос вида "whois -h whois.iana.org <домен верхнего уровня>" и получить, среди прочей информации, название нужного whois сервера. Если IANA вернула лишь адрес сайта, whois сервер с определенной вероятностью может быть «угадан» как whois.<адрес сайта без www>.
  2. С достаточно большой вероятностью название whois сервера может иметь вид whois.nic.<домен верхнего уровня> или whois.<домен верхнего уровня>.
  3. Многие домены второго уровня имеют свои собственные whois серверы. В подавляющем большинстве случаев, их названия имеют вид whois.<домен второго уровня> или whois.nic.<домен второго уровня>.
  4. Для отдельных доменов верхнего уровня работает алиас <домен верхнего уровня>.whois-servers.net.

Таким образом, для каждого домена у нас будет целый список потенциальных whois серверов с большей или меньшей вероятностью работоспособности. Их можно опрашивать в порядке приоритета, запоминая при этом whois серверы, которые не работают (или которых не существует), чтобы не обращаться к ним при следующих запросах.

Результат, полученный от первого whois сервера, может содержать ссылку на другой whois сервер с дополнительной информацией. Чаще всего, она будет выглядеть как «whois server: <название>», однако в отдельных случаях может выглядеть и по-другому. Здесь нужно не забывать о том, что многие whois серверы бывают доступны под несколькими алиасами, и найденное название может быть не дополнительным whois сервером, а просто другим алиасом первого сервера (на практике, whois серверы часто включают в результат свое название).

С IP адресами ситуация другая. Есть пять основных региональных whois серверов:

  • whois.arin.net (ARIN, Северная Америка)
  • whois.apnic.net (APNIC, Азия и Тихоокеанский регион)
  • whois.ripe.net (RIPE, Европа и Ближний Восток)
  • whois.afrinic.net (AfriNIC, Африка)
  • whois.lacnic.net (LACNIC, Латинская Америка)

Информация про любой IP адрес (как IPv4, так и IPv6) должна быть на одном из них. Если же какой-то диапазон IP адресов ни одному из региональных серверов еще не выделен, то информация об этом должна быть на whois.iana.org. Все региональные whois серверы можно опрашивать по порядку, хотя во многих случаях «неправильный» whois сервер сразу же будет содержать ссылку на «правильный». Первый запрос можно посылать на ARIN, так как он отвечает за наибольшее число IP адресов. Результаты, полученные от одного из региональных whois серверов, также могут содержать ссылки на дополнительные whois серверы.

Стоит помнить, что whois.ripe.net и whois.afrinic.net могут легко забанить IP адрес, с которого приходит слишком много запросов. Кроме того, whois.lacnic.net работает как прокси и, в случае необходимости, отправляет запросы на все остальные региональные whois серверы (естественно, увеличивая «счетчики» на whois.ripe.net и whois.afrinic.net).

В подавляющем большинстве случаев правильный запрос к whois серверу должен выглядеть как "<домен или IP адрес>\r\n", однако некоторые whois серверы требуют другой формат запроса (детали смотреть в разделе "Как правильно отправить запрос?").

Некоторые whois серверы работают по альтернативному протоколу, который называется rwhois («Referral Whois») и использует порт 4321 (смотреть http://en.wikipedia.org/wiki/Whois#Referral_Whois и http://tools.ietf.org/html/rfc2167). Его реализация достаточно сложна, поэтому для работы с rwhois серверами рекомендуется использовать уже имеющиеся решения (например, http://projects.arin.net/rwhois/). При этом стоит помнить, что многие серверы, которые идентифицируют себя как rwhois, на самом деле работают по протоколу обычного whois! Упомянутый выше rwhois клиент этого не подозревает и выдает ошибку.

Когда результат с whois сервера наконец-то получен, встает задача отличить валидный результат от сообщения об ошибке. На практике сделать это достаточно непросто, так как у разных whois серверов сообщение об ошибке может выглядеть как угодно. Алгоритм, который используется в моем модуле, детально описан в разделе "Как обработать полученный результат?".

Нужно помнить, что многие whois серверы включают в результат IP адрес, с которого был сделан запрос. Также некоторые whois серверы используют в своих результатах HTML тэги. Большинство whois серверов возвращают результат в кодировке utf8, однако японские и корейские whois серверы используют iso-2022-jp и euc-kr соответственно.

Вместо заключения


Вероятно, whois не самая востребованная технология, иначе полезной информации в Интернете было бы гораздо больше. Тем не менее, уверен, что найдутся люди, которым данная статья поможет сэкономить ощутимое количество времени и нервов.
@arukraine
карма
39,0
рейтинг 0,0
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама

Самое читаемое Разработка

Комментарии (29)

  • –11
    Ты whois, давай до свидания…
    Автору спасибо, много интересного из простой вещи.
  • +8
    Фундаментально! И читается как детектив.

    Автору — респект.
  • +4
    Отличная статья. Самому всегда было лень разобраться, гугл не давал ответа, но теперь многие непонятности стали понятны.
  • 0
    Посмотрите на pwhois, фактически это обвязка для перлового модуля Net::Whois::Raw, про зону UA он точно знает, причём в нём есть whois-сервера и многих зон 3-го уровня:
    COM.UA whois.com.ua
    ORG.UA whois.com.ua
    BIZ.UA whois.biz.ua
    CO.UA whois.co.ua
    PP.UA whois.pp.ua
    KIEV.UA whois.com.ua
    DN.UA whois.dn.ua
    LG.UA whois.lg.ua
    OD.UA whois.od.ua
    IN.UA whois.in.ua

    В федоре whois является ссылкой на jwhois, а иногда попадается что whois является ссылкой и на pwhois.
    • 0
      pwhois/Net::Whois::Raw посмотрел — благодарю за ссылку.

      Он работает так же, как и Ruby Whois: огромный конфигурационный файл с хардкодом на все случаи жизни (порядка 70КБ). На CPAN последнее обновление — август 2012 года.

      Пробуем примеры из моей статьи:

      $ ./pwhois 8.8.8.8
      [whois.arin.net]
      
      Level 3 Communications, Inc. LVLT-ORG-8-8 (NET-8-0-0-0-1) 8.0.0.0 - 8.255.255.255
      Google Incorporated LVLT-GOOGL-1-8-8-8 (NET-8-8-8-0-1) 8.8.8.0 - 8.8.8.255
      

      $ ./pwhois russia.edu.ru
      [whois.ripn.net]
      
      No entries found for the selected source(s).
      
      Last updated on 2013.01.15 18:21:31 MSK
      

      Всего не захардкодить.

      Что меня заинтересовало: захардкоджено много сообщений с разных серверов о том, что «Lookup quota exceeded». В Ruby Whois они тоже есть, но там информация раскидана по разных файлам (для каждого whois сервера), и ее трудно найти, а тут все в одном месте. Получается, что пользователей банят не только RIPE и AfriNIC, но и целый ряд других whois серверов (на практике я с этим раньше не встречался):

      our %exceed = (
          'whois.eu' => '(?:Excessive querying, grace period of|Still in grace period, wait)',
          'whois.dns.lu' => 'Excessive querying, grace period of',
          'whois.mynic.net.my' => 'Query limitation is',
          'whois.ripn.net' => 'exceeded allowed connection rate',
          'whois.domain-registry.nl' => 'too many requests',
          'whois.nic.uk' => 'and will be replenished',
          'whois.networksolutions.com' => 'contained within a list of IP addresses that may have failed',
          'whois.worldsite.ws' => 'You exceeded the maximum',
          'whois.tucows.com'  => '(?:Maximum Daily connection limit reached|exceeded maximum connection limit)',
          'whois.centralnic.com'  => 'Query rate of \\d+',
          'whois.pir.org'  => 'WHOIS LIMIT EXCEEDED',
          'whois.nic.ms'   => 'Look up quota exceeded',
          'whois.nic.gs'   => 'look up quota exceeded',
          'whois.nic.tl'   => 'Lookup quota exceeded',
          'whois.nic.mg'   => 'Lookup quota exceeded',
          'whois.nic.li'   => 'You have exceeded this limit',
          'whois.nic.ch'   => 'You have exceeded this limit',
      );
      
      • 0
        > pwhois russia.edu.ru

        Если написать автору, то или прислать патч, то будет весьма оперативно добавлено.
        Мои правки там тоже есть, например те же .*.ua сервера.
    • 0
      Дописал в статью несколько слов про pwhois.
  • +1
    И ещё одно:
    если под рукой нет whois утилиты, при этом Вы знаете whois сервер, но не хотите писать для опроса свой скрипт, то можно для запроса использовать telnet:
    $ telnet whois.com.ua 43
    Trying 217.12.194.25…
    Connected to whois.com.ua.
    Escape character is '^]'.
    % request from 1.2.3.4
    % This is the Ukrainian Whois query server #F.
    % The Whois is subject to Terms of use
    % See hostmaster.ua/services/
    %

    kiev.ua <=== тут вводится имя домена

    % % .UA whois
    % Domain Record:
    % =============
    domain: kiev.ua
    admin-c: UADM15-UANIC
    tech-c: HOST-UANIC
    dom-public: YES
    nserver: sns-pb.isc.org
    nserver: ho1.ns.kiev.ua
    nserver: k.ns.com.ua
    nserver: ba1.ns.ua
    nserver: nix.ns.ua
    remark: Public domain
    remark: Kiev, Ukraine
    created: 0-UANIC 19921202000000
    changed: ST101-UANIC 20120726111700
    source: UANIC

    % Glue Record:
    • +2
      Да, для работы с whois в «ручном» режиме, telnet — это все, что требуется.

      Более того, некоторые whois серверы поддерживают специальные параметры, которые через Unix-овый whois или другую утилиту передать не удается. Так что остается только telnet.

      Например, параметр "-B" позволяет whois.ripe.net выводить поля «notify», «changed» и «e-mail», которые по-умолчанию не выводятся:

      $ telnet whois.ripe.net 43
      Trying 193.0.6.135...
      Connected to whois.ripe.net.
      Escape character is '^]'.
      % This is the RIPE Database query service.
      % The objects are in RPSL format.
      %
      % The RIPE Database is subject to Terms and Conditions.
      % See http://www.ripe.net/db/support/db-terms-conditions.pdf
      
      -B 213.180.204.0                    <<<<< обратите внимание на "-B"
      % Information related to '213.180.204.0 - 213.180.204.255'
      
      inetnum:        213.180.204.0 - 213.180.204.255
      netname:        YANDEX-213-180-204
      descr:          Yandex enterprise network
      country:        RU
      admin-c:        YNDX1-RIPE
      tech-c:         YNDX1-RIPE
      remarks:        INFRA-AW
      status:         ASSIGNED PA
      mnt-by:         YANDEX-MNT
      changed:        artem@yandex-team.ru 20091116          <<<<< дополнительное поле
      source:         RIPE
      ...
      
  • +3
    Не будете ли вы столь щедры поделиться исходниками своего python модуля? Спасибо!
    • 0
      он же писал что код проприетарный. Лучше воспользоваться тем же Net::Whois::Raw. Там всё открыто. Строк больше, но и функций тоже.
    • +2
      Модуль писался для коммерческого проекта, поэтому код, к сожалению, проприетарный. Однако все, что в нем было, я постарался описать в статье.
  • +2
    Редкая статья на хабре нынче так радует. Было очень приятно почитать, спасибо.
  • 0
    rdap.org/ — частично многие данные можно уже получить в JSON формате. Это наработки WIERDS рабочей группы, которая разрабатывает замену WHOIS, используя restfull интерфейс.
  • +1
    Все-таки при поиске IP, лучше сразу опрашивать IANA, а не ARIN. Это из личного опыта. Тоже в свое время страдал этой проблемой, когда делал этот инструмент (IP whois only)
    • 0
      Возможно. Я первым спрашиваю ARIN из следующих соображений: 1. за большинство выделенных IP (и за подавляющее большинство IP, которые могут интересовать потенциальных пользователей нашего продукта) отвечает именно ARIN. 2. ARIN никого не банит.

      Если бы сайт был в рунете (где за все IP адреса отвечает RIPE), вероятно, опрашивать ARIN первым имело бы меньше смысла.
      • 0
        IANA во всей иерархии — высший орган. Именно эта организация отвечает за распределение блоков региональным реестрам. ARIN лишь один из них. Поэтому запрос к whois IANA всегда будет возвращать реферал на нужный RIR. Проблем с баном пока не было.
        • 0
          Идея в том, чтобы минимизировать число возможных запросов (а значит и время ожидания пользователя).

          Если я обращаюсь к ARIN, то (условно) в 75% случаев я получу требуемый результат с первого же запроса, в 24% случаев со второго (ARIN почти всегда указывает на правильный региональный сервер) и в 1% случаев с третьего или больше. Если же я обращаюсь к IANA, то мне всегда надо будет делать минимум два запроса. Минимум — потому что IANA тоже не всегда указывает на актуальный сервер. Например:

          $ whois -h whois.iana.org 213.154.64.0
          
          refer:        whois.ripe.net
          
          inetnum:      213.0.0.0 - 213.255.255.255
          organisation: RIPE NCC
          status:       ALLOCATED
          
          whois:        whois.ripe.net
          
          changed:      1993-10
          source:       IANA
          


          Если теперь обратиться к RIPE, то окажется, что IP адрес переделегирован AfriNIC, о чем IANA не знала. Стоит отметить, что ARIN также указывает на RIPE.
          • 0
            Я понимаю, что вы так оптимизируете для своего частного случая. Я же говорю — в целом, имело бы смысл начинать с IANA, для общего случая. Комметнарии читают и другие и не все ориентируются именно на ARIN. Плюс ко всему, в APNIC делегирование происходит национальным регистрам — JPNIC, KRNIC и т.д. — их там довольно много, я еще сам у себя не все сделал. В целом, для общего случая, если речь идет о равности всех клиентов, то лучше начинать сверху и идти вниз, не взирая на лишний запрос.
  • 0
    Респект за статью. Давно хотел разобраться как же работает этот самый whois. Было много вопросов, но никак не было времени. Теперь все более менее понятно.
  • 0
    Отличная статья, спасибо!
  • 0
    Есть ли возможность узнать количество всех записей (проданных доменов) в каком-либо домене верхнего уровня? (нужен домен .tv, сервер tvwhois.verisign-grs.com)
    • 0
      Через протокол whois — точно нет. Возможно, такая информация есть на сайтах некоторых регистраторов. Ну или кто-то достал такую статистику и еще где-то выложил.
    • 0
      Есть за деньги на zone-file.com
  • 0
    Есть маленькая тулза для определения whois серверов доменных зон и whois произвольного домена в каждой из выбранных зон. Написана на PHP, может кому пригодится: github.com/noys/ianacheck
  • 0
    Как порешали вопрос с зоной .gr?
    • 0
      В чем именно была проблема с зоной .gr? Я уже точно не помню, но вроде бы 2.5 года назад (на момент написания статьи) ее не было. Хотя с некоторыми зонами они действительно были нерешаемые: если информация доступна только на вебе с каптчей, то вряд ли что-то можно придумать.

      Благодарю!
      • 0
        Да, именно такая ситуация с gr — веб и каптча.

        За статью спасибо, очень помогло )

        ps. точнее решение есть, но надо становиться регистратором (вроде как). а это наверное слегка затратно.
  • 0
    Спасибо. Долго искал информацию про эту мудреную конструкцию:
    $ whois -h whois.nic.name 'domain = d-n-s.name'

Только зарегистрированные пользователи могут оставлять комментарии. Войдите, пожалуйста.