Статья Получение WHOIS-информации о домене по его IP-адресу с помощью Python

При проведении различного рода исследований связанных с доменами, иногда необходимо узнать информацию WHOIS по их IP-адресу. Безусловно, в интернете существует большое множество сервисов, которые предоставляют данную услугу. Но, что, если вам необходимо проверить большое количество адресов? Иногда у сервисов есть специальный API для выполнения такого рода проверок. Вот только доступ к таким API либо, предоставляется с большими ограничениями, либо является и вовсе платным.
В частности для Python существуют библиотеки, которые выполняют запросы такого характера. Но, почему-то, из всех, на которые я натыкался довольно часто, самыми популярными являются python-whois и ipwhois. В данной статье я попробую сравнить эти две библиотеки, а также мы попробуем получить информацию о whois самостоятельно, путем написания собственных функций для этих целей.

000.jpg

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


Для начала определим сайт, о котором будем запрашивать информацию. В данном случае я ничего не выбирал, а сайт, который попался под руку, был совершенно рандомным. Итак – это . Как видим, его доменное имя webdevblog.ru. Идем на сайт , для получения исторической информации по данному домену. Так как за время своего существования сайт может хоститься у совершенно разных провайдеров или просто использовать CDN. Запросим информацию по данному домену и видим, что самый первый адрес данного сайта «46.17.40.108». Вот по нему мы и запросим информацию WHOIS.

screenshot4.png

Двигаемся дальше. Бесспорно, одним из лучших сервисов по получению информации WHOIS о домене или IP-адресе домена, является whois.domaintools.com. Вот на него и сходим. Можно сказать, что в данном случае он будет служить нам эталоном для сравнения полученной информации из других источников.
Итак, делаем запрос по IP-адресу и получаем следующую информацию:

screenshot1.png

Здесь нужно сказать, что данную информацию можно получить и при использовании сервиса .

screenshot3.png

Однако здесь есть небольшое различие, а именно, на reg.ru не указан whois-сервер, с которого получалась информация. В то время, как у whois.domaintools.com данная информация присутствует.

screenshot2.png


Вот на эту информацию мы и будем ориентироваться.
Для начала давайте получим информацию об IP-адресе с помощью библиотек python-whois и ipwhois. И посмотрим, насколько результаты выдачи отличаются от тех, что мы будем считать эталонными.


Что потребуется?

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

pip install python-whois ipwhois

Двигаемся дальше. Создадим файл скрипта, для примера whois_info.py и импортируем в него установленные библиотеки. Так как данные библиотеки возвращают данные в виде словаря, используем pprint для удобного вывода словарей в терминале.

Python:
from pprint import pprint

import ipwhois
import whois


Получаем информацию об IP-адресе

Не будем подробно описывать создание каждой из функций. Так как они довольно малы.

Создадим функцию ipwhois_info(ip), которая принимает на вход ip-адрес. Создадим переменную results, в которую будет возвращаться результат запроса. Присвоим данной переменной объект IPWhois, в который передадим адрес и выполним функцию lookup_whois. После чего распечатаем результат.

Создадим еще одну функцию, whois_info(ip), которая на вход также получает ip-адрес. В переменную results получим результат запроса и также распечатаем результат.

Python:
def ipwhois_info(ip):
    results = ipwhois.IPWhois(ip).lookup_whois()
    pprint(results)
    print("\n\n")


def whois_info(ip):
    results = whois.whois(ip)
    pprint(results)


ipwhois_info("46.17.40.108")
whois_info("46.17.40.108")

Посмотрим, что у нас получилось.

Код:
{'asn': '51659',
 'asn_cidr': '46.17.40.0/23',
 'asn_country_code': 'RU',
 'asn_date': '2010-10-14',
 'asn_description': 'ASBAXET, RU',
 'asn_registry': 'ripencc',
 'nets': [{'address': 'Zelenograd, Sosnovaya alleya, 4, str 2, 33\n'
                      'Moscow, Russia',
           'cidr': '46.17.40.0/23',
           'city': None,
           'country': 'RU',
           'created': '2011-05-20T07:20:59Z',
           'description': 'LLC BAXET',
           'emails': None,
           'handle': 'AP12753-RIPE',
           'name': 'BX-NETWORK',
           'postal_code': None,
           'range': '46.17.40.0 - 46.17.41.255',
           'state': None,
           'updated': '2023-04-06T16:14:15Z'},
          {'address': None,
           'cidr': '46.17.40.0/23',
           'city': None,
           'country': None,
           'created': '2011-05-19T11:23:31Z',
           'description': 'LLC BAXET',
           'emails': None,
           'handle': None,
           'name': None,
           'postal_code': None,
           'range': '46.17.40.0 - 46.17.41.255',
           'state': None,
           'updated': '2011-05-19T11:23:31Z'}],
 'nir': None,
 'query': '46.17.40.108',
 'raw': None,
 'raw_referral': None,
 'referral': None}

Как видим, ничего интересного. То есть, какая-то часть информации безусловно соответствует той, что мы получили на эталонном сервисе. Но, она далеко не вся. Посмотрим, может быть результат выполнения запроса другой библиотеки окажется информативнее?

Код:
{'creation_date': datetime.datetime(2022, 4, 18, 11, 19, 23),
 'dnssec': 'unsigned',
 'domain_name': 'kmwcfcl.cn',
 'emails': 'service7-24@outlook.com',
 'expiration_date': datetime.datetime(2024, 4, 18, 11, 19, 23),
 'name': '孙斌',
 'name_servers': ['expirens4.hichina.com', 'expirens3.hichina.com'],
 'registrar': '阿里云计算有限公司(万网)',
 'status': 'ok'}

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

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

Python:
# pip install python-whois ipwhois PySocks
# pip install ipwhois

from pprint import pprint

import ipwhois
import whois


def ipwhois_info(ip):
    results = ipwhois.IPWhois(ip).lookup_whois()
    pprint(results)
    print("\n\n")


def whois_info(ip):
    results = whois.whois(ip)
    pprint(results)


ipwhois_info("46.17.40.108")
whois_info("46.17.40.108")


Создаем собственный скрипт

Для работы скрипта установки сторонних библиотек не потребуется. Воспользуемся только теми библиотеками, которые python предоставляет «из коробки». Поэтому, импортируем их в скрипт:

Python:
import socket
import time
from datetime import datetime
from ipaddress import IPv4Address, AddressValueError
from pprint import pprint

С помощью библиотеки socket мы будем выполнять запросы для получения информации. datetime – необходим для перевода даты в более удобочитаемый формат. IPv4Address – мы будем использовать для валидации ip-адреса. Ну и pprint – не обязательная, но в данном скрипте используемая библиотека для удобочитаемого вывода словаря в терминал.

Для начала немного теории. Есть такой whois сервер whois.iana.org. Он содержит свежую информацию обо всех доменах верхнего уровня. В нашем же случае, мы будем получать информацию о whois-сервере, куда и будем выполнять основной запрос. Однако для некоторых доменов IANA информацию не предоставляет. С чем это связано, гадать не буду. Может быть просто регистраторы некоторых стран ее просто не делают общедоступной.

Сделаем запрос к whois.iana.org и вот что он возвращает:

screenshot7.png

Здесь нас интересует строка:

Код:
'whois:        whois.ripe.net\n'

Именно она содержит информацию о сервере whois. Поэтому, нам нужно ее получить из вывода и передать в основную функцию для получения информации по ip-адресу.


Получение whois-сервера с whois.iana.org

Создадим функцию ianna(ip). На вход данной функции мы будем передавать интересующий нас ip-адрес. Создадим сокет, установим соединение на порт 43 и отправим запрос с интересующими нас данными. После чего получим ответ, декодируем и заберем из него строку, в которой содержится информацию о whois. Если такой информации не окажется – вернем False.

Python:
def ianna(ip):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("whois.iana.org", 43))
    s.send((ip + "\r\n").encode())
    response = b""
    while True:
        data = s.recv(4096)
        response += data
        if not data:
            break
    s.close()
    whois = ''
    for resp in response.decode().splitlines():
        if resp.startswith('%') or not resp.strip():
            continue
        elif resp.startswith('whois'):
            whois = resp.split(":")[1].strip()
            break
    return whois if whois else False


Получаем информацию по ip-адресу

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

А далее, действуем так же, как и в предыдущей функции. Создаем сокет, устанавливаем соединение с сервером на порт 43. Отправляем запрос с интересующим нас ip-адресом и забираем ответ сервера. После чего, декодируем его, делим на строки, забираем все строки содержащие информацию. Формируем словарь и возвращаем из функции.

Python:
def get_whois(ip, whois):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((whois, 43))
    s.send((ip + "\r\n").encode())
    response = b""
    while True:
        data = s.recv(4096)
        response += data
        if not data:
            break
    s.close()
    whois_ip = dict()
    num = 0
    for ln in response.decode().splitlines():
        if ln.strip().startswith("%") or not ln.strip():
            continue
        else:
            if ln.strip().split(": ")[0].strip() in ['created', 'last-modified']:
                dt = datetime.fromisoformat(ln.strip().split(": ")[1].strip()).strftime("%Y-%m-%d %H:%M:%S")
                whois_ip.update({f'{ln.strip().split(": ")[0].strip()}_{num}': dt})
                num += 1
            else:
                whois_ip.update({ln.strip().split(": ")[0].strip(): ln.strip().split(": ")[1].strip()})
    return whois_ip if whois_ip else False


Валидация адреса и обработка ответов функций

Создадим функцию validate_request(ip). На вход она получает целевой ip-адрес. Затем мы передаем полученный адрес в функцию IPv4Address. И, если полученная строка не будет им являться, возникнет исключение, которое мы и обработаем. То есть, выведем информацию о том, что введенный адрес не валидный. Если же все в порядке, получаем данные из функции ianna. И, если вернулся whois-сервер, двигаемся дальше, а именно, выполняем функцию get_whois, куда передаем целевой ip и полученный сервер. Если функция возвращает словарь с данными, выводим его на печать в терминал. В противном случае, если не было получено информации, сообщаем об этом пользователю.

Впрочем, здесь выполняется условие else, в котором мы выполняем запрос на сервер 'whois.ripe.net'. Однако, это только лишь в качестве эксперимента, так как требует более детального изучения и экспериментов. Но, тем не менее, почему бы не сделать такую попытку?

Python:
def validate_request(ip):
    try:
        IPv4Address(ip)
        if whois := ianna(ip):
            time.sleep(1)
            if info := get_whois(ip, whois):
                pprint(info)
            else:
                print("No IP address data has been received.")
        else:
            print("I can't get information about the registrar. The registrar whois.ripe.net will be used.")
            if info := get_whois(ip, 'whois.ripe.net'):
                pprint(info)
            else:
                print("No IP address data has been received.")
    except AddressValueError:
        print("IP-address not valid")
    except ConnectionResetError as ex:
        print(ex)

Теперь выполним наши скрипт и посмотрим, что он нам вернет по тому ip-адресу, который мы определили для изучения.

screenshot5.png

Как мы видим, теперь была возвращена интересующая нас информация. А значит, в данном случае мы на верном пути. Давайте попробуем выполнить запрос по еще какому-нибудь адресу. В качестве подопытного возьмем habr.com. Идем на сайт для получения исторической информации по данному домену.

screenshot8.png

Теперь сходим на whois.domaintools.com и получим информацию по последнему адресу в данном списке: 82.98.86.175.

screenshot9.png

И да, здесь довольно много всякого разного. Но, для себя выделим вот этот блок:

screenshot10.png


Теперь выполним запрос по тому же ip-адресу в нашем скрипте и посмотрим, что получилось:

screenshot6.png

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

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

Ну и напоследок. Данный скрипт можно слегка модифицировать, так как выполняемые запросы с помощью socket однотипны, а различается лишь способ обработки полученной информации. Поэтому, обработку информации можно вынести в отдельную функцию. Таким образом, получиться вот такой скрипт:

Python:
import socket
import time
from datetime import datetime
from ipaddress import IPv4Address, AddressValueError
from pprint import pprint


def data_processing(response, whois=''):
    if whois == "whois.iana.org":
        whois_d = ""
        for resp in response.splitlines():
            if resp.startswith('%') or not resp.strip():
                continue
            elif resp.startswith('whois'):
                whois_d = resp.split(":")[1].strip()
                break
        return whois_d if whois_d else False
    else:
        whois_ip = dict()
        num = 0
        for ln in response.splitlines():
            if ln.strip().startswith("%") or not ln.strip():
                continue
            else:
                if ln.strip().split(": ")[0].strip() in ['created', 'last-modified']:
                    dt = datetime.fromisoformat(ln.strip().split(": ")[1].strip()).strftime("%Y-%m-%d %H:%M:%S")
                    whois_ip.update({f'{ln.strip().split(": ")[0].strip()}_{num}': dt})
                    num += 1
                else:
                    whois_ip.update({ln.strip().split(": ")[0].strip(): ln.strip().split(": ")[1].strip()})
        return whois_ip if whois_ip else False


def get_whois(ip, whois):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((whois, 43))
    s.send((ip + "\r\n").encode())
    response = b""
    while True:
        data = s.recv(4096)
        response += data
        if not data:
            break
    s.close()
    data = data_processing(response.decode(), whois)
    return data


def validate_request(ip):
    try:
        IPv4Address(ip)
        if whois := get_whois(ip, 'whois.iana.org'):
            time.sleep(1)
            if info := get_whois(ip, whois):
                pprint(info)
            else:
                print("No IP address data has been received.")
        else:
            print("I can't get information about the registrar. The registrar whois.ripe.net will be used.")
            if info := get_whois(ip, 'whois.ripe.net'):
                pprint(info)
            else:
                print("No IP address data has been received.")
    except AddressValueError:
        print("IP-address not valid")
    except ConnectionResetError as ex:
        print(ex)


validate_request('82.98.86.175')

Python:
import socket
import time
from datetime import datetime
from ipaddress import IPv4Address, AddressValueError
from pprint import pprint


def ianna(ip):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect(("whois.iana.org", 43))
    s.send((ip + "\r\n").encode())
    response = b""
    while True:
        data = s.recv(4096)
        response += data
        if not data:
            break
    s.close()
    whois = ''
    for resp in response.decode().splitlines():
        if resp.startswith('%') or not resp.strip():
            continue
        elif resp.startswith('whois'):
            whois = resp.split(":")[1].strip()
            break
    return whois if whois else False


def get_whois(ip, whois):
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((whois, 43))
    s.send((ip + "\r\n").encode())
    response = b""
    while True:
        data = s.recv(4096)
        response += data
        if not data:
            break
    s.close()
    whois_ip = dict()
    num = 0
    for ln in response.decode().splitlines():
        if ln.strip().startswith("%") or not ln.strip():
            continue
        else:
            if ln.strip().split(": ")[0].strip() in ['created', 'last-modified']:
                dt = datetime.fromisoformat(ln.strip().split(": ")[1].strip()).strftime("%Y-%m-%d %H:%M:%S")
                whois_ip.update({f'{ln.strip().split(": ")[0].strip()}_{num}': dt})
                num += 1
            else:
                whois_ip.update({ln.strip().split(": ")[0].strip(): ln.strip().split(": ")[1].strip()})
    return whois_ip if whois_ip else False


def validate_request(ip):
    try:
        IPv4Address(ip)
        if whois := ianna(ip):
            time.sleep(1)
            if info := get_whois(ip, whois):
                pprint(info)
            else:
                print("No IP address data has been received.")
        else:
            print("I can't get information about the registrar. The registrar whois.ripe.net will be used.")
            if info := get_whois(ip, 'whois.ripe.net'):
                pprint(info)
            else:
                print("No IP address data has been received.")
    except AddressValueError:
        print("IP-address not valid")
    except ConnectionResetError as ex:
        print(ex)


validate_request('46.17.40.108')

А на этом, пожалуй, все.

Спасибо за внимание. Надеюсь, данная информация будет вам полезна
 

Вложения

  • whois_get.zip
    1,9 КБ · Просмотры: 166
Последнее редактирование модератором:

TR1X

Green Team
04.04.2020
220
156
BIT
134
Прикольно :) Можно прикрутить какой нибудь Flask, вывести все в html, поднять на каком нибудь хостинге и вот тебе свой сервис )
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!