Самодельный Dynamic DNS

    Статья о том, как за несколько минут своими руками сделать Dynamic DNS с помощью Perl, Yandex DNS API и роутера D-Link.

    Многие роутеры D-Link поддерживают встроенную функцию Dynamic DNS.
    К сожалению, бесплатно доступны лишь домены вида example.dlinkddns.com.

    Есть также очень удобное DNS API от Яндекса.

    Этим сочетанием мы и воспользуемся.

    Для начала нужно подключить домен к сервису Яндекса — pdd.yandex.ru/domains_add.
    Далее получаем для нашего домена токен, для этого нужно перейти по ссылке вида:
    pddimp.yandex.ru/get_token.xml?domain_name=example-site.ru
    (example-site.ru — имя домена).

    Получили токен — нечто вроде «gjkgwrth34wjh45kj2th234jkht34234lkj5».
    Далее — переходим по ссылке вида:
    pddimp.yandex.ru/nsapi/get_domain_records.xml?token=gjkgwrth34wjh45kj2th234jkht34234lkj5&domain=example-site.ru
    Получаем список записей DNS-зоны нашего домена в формате XML, нечто вроде следующего:

    <page>
      <domains>
        <domain>
          <name>example-site.ru</name>
          <response>
            <record domain="example-site.ru" priority="" ttl="21600" subdomain="@" type="A" id="23232301">3.5.7.9</record>
            <record domain="www.example-site.ru" priority="" ttl="21600" subdomain="www" type="A" id="23232302">3.5.7.9</record>
          </response>
        </domain>
        <error>ok</error>
      </domains>
    </page>
    


    Из этого отклика нам нужны аттрибуты id тэгов record.

    Используя все это, можем написать perl-скрипт для обновления IP.

    use LWP::UserAgent;
    
    my $hostout = `host example.dlinkddns.com`; //адрес нашего роутера
    
    if ($hostout =~ /(\d+)\.(\d+)\.(\d+)\.(\d+)/) {
      my $ip = "$1.$2.$3.$4";
    
       #Добавлено:
      open (FILE,"my_ip.txt");
      my @lines = <FILE>;
      $old_ip = $lines[0]; #Считываем IP из файла
      $old_ip =~ s/^\s+|\s+$//g; #trim
      close(FILE);
      if ($old_ip eq $ip) {
        die "IP not changed"; # Выходим из скрипта, если IP не изменился
      }
      open (FILE,">my_ip.txt");
      print FILE $ip; # Записываем в файл новый IP
      close(FILE);
    
      my $token = "gjkgwrth34wjh45kj2th234jkht34234lkj5";
      my $domain00 = "example-site.ru";
      my $id00 = "23232301";
      my $subdomain01 = "www";
      my $id01 = "23232302";
      my $url00 ="https://pddimp.yandex.ru/nsapi/edit_a_record.xml?token=$token&domain=$domain00&record_id=$id00&content=$ip";
      my $url01 ="https://pddimp.yandex.ru/nsapi/edit_a_record.xml?token=$token&domain=$domain00&subdomain=$subdomain01&record_id=$id01&content=$ip";
    
      my $ua = LWP::UserAgent->new( ssl_opts => { verify_hostname => 0 } );
    
      my $response = $ua->get($url00);   
      if ( $response->is_success ) {
        print $response->decoded_content;
      } else {
        die $response->status_line;
      }
    
      my $response = $ua->get($url01);
      if ( $response->is_success ) {
        print $response->decoded_content;
      } else {
        die $response->status_line;
      }
    }
    


    Осталось только прописать в /etc/crontab строчку вроде
    */5 * * * * root perl /root/update_my_ip.pl
    (для обновления каждые 5 минут, update_my_ip.pl — имя нашего скрипта).
    Метки:
    Поделиться публикацией
    Реклама помогает поддерживать и развивать наши сервисы

    Подробнее
    Реклама
    Комментарии 28
    • +1
      Для начала нужно подключить домен к сервису Яндекса — pdd.yandex.ru/domains_add/.

      Я не понял, подключать надо домен вида example.dlinkddns.com или фигурирующий далее по тексту example-site.ru?
      • 0
        В Яндексе подключается example-site.ru.
        example.dlinkddns.com используется для определения внешнего адреса роутера.
        • 0
          Это как так? )
          У вас получается второй уровень динамического ДНС: первый — dlinkddns, второй — ваш самодельный.
          Внешний адрес всё же надо как-то по-другому определять, чтобы отпала надобность в dlinkddns.com, иначе какой смысл?
          • 0
            Догадываюсь, что внешний адрес можно определить по-другому.
            Но, пожалуй, для счастливых обладателей роутеров D-Link этот способ самый простой.

            А смысл — направить наш хост example-site.ru на IP-адрес нашего роутера.
            • +1
              Нашел еще один способ: curl ifconfig.me

              Сокращает код на пару строк.
              Но опять же, обращение к внешнему сайту.
              Плюс несколько минут пришлось погуглить )
              • 0
                Кстати, в плане скорости.
                `host example.dlinkddns.com` — срабатывает за доли секунды.
                `curl ifconfig.me` — выполняется 10-15 секунд
                • +1
                  ifconfig.me — это видимо какой-то супер-хайлоад сервис, испытывающий нереальные нагрузки, раз он открывается десятки секунд :)
                  Я думал изначальная задача была отказаться от dlinkddns, но если вам нужна просто днс-запись example.dlinkddns.com то `host example.dlinkddns.com` вполне себе решение.
                  • 0
                    Там, кстати, грамотно настроена балансировка к тому же.
                    # curl -v ifconfig.me
                    * About to connect() to ifconfig.me port 80 (#0)
                    *   Trying 49.212.149.105...
                    * Connection refused
                    *   Trying 219.94.235.40...
                    * Connected to ifconfig.me (219.94.235.40) port 80 (#0)
                    > GET / HTTP/1.1
                    > User-Agent: curl/7.29.0
                    > Host: ifconfig.me
                    > Accept: */*
                    > 
                    < HTTP/1.1 200 OK
                    < Date: Wed, 08 Oct 2014 21:09:27 GMT
                    < Server: Apache
                    < Vary: Accept-Encoding
                    < Connection: close
                    < Transfer-Encoding: chunked
                    < Content-Type: text/plain
                    < 
                    2XX.1XX.1XX.XX
                    * Closing connection 0
                    #
                    
                  • 0
                    Можно использовать еще:
                    wget -qO- ip-api.com/line/?fields=query
          • +1
            Вот только вопрос в том, как менять значения IP в Яндекс Домене?
            В этом и смысл DynDNS, что сервис следит за вашим IP адресом и прописывает его каждый раз когда он обновляется (например при каждом реконетке или еще как там настроено у провайдера)
            • 0
              Переход по такой ссылке обновляет IP на Яндексе:
              pddimp.yandex.ru/nsapi/edit_a_record.xml?token=$token&domain=$domain00&record_id=$id00&content=$ip
              $domain00 — домен
              $ip — IP-адрес

              Можно немного усложнить Perl-скрипт — к примеру, записывать при каждом обновлении IP-адрес в файл, считывать его в начале скрипта и отправлять запросы Яндексу только в том случае, если он изменился.
            • +1
              Внимательно читаем XML от API Яндекса:
              <record domain=«example-site.ru» priority="" ttl=«21600» subdomain="@" type=«A» id=«23232301»>3.5.7.9</record>

              И видим, что ttl=«21600», т.е. данная запись может кэшироваться другими DNS серверами на 6 часов.

              PS: Именно за малый TTL всевозможные DDNS сервисы деньги с людей берут.
              • 0
                Недоглядел немного.

                api.yandex.ru/pdd/doc/reference/api-dns_edit_a_record.xml
                ttl можно при редактировании записи в качестве параметра указать.
                21600 — значение по умолчанию.
                • 0
                  TTL там давно уже редактируется, правда он один на всю зону.
                  Руками один раз в морду придется залезть, правда.
                  • 0
                    Специально проверил, прекрасно меняется скриптом отдельно для каждой записи.
                    • 0
                      host-ом тоже спросили?
                      • 0
                        Проверил XML-ответ после запуска скрипта.
                        TTL в нужных записях изменился, в других остался по умолчанию.
              • 0
                А нельзя прикрутить скрипт к /etc/networking? То есть на xWRT прошивках (либо альтернативных прошивках ASUS) все можно сделать внутри роутера. Тогда, собственно, в случае «белого» ip и проверка внешнего не нужна.
                Или мы говорим про две сущности — на роутере чисто канал и на компьютере с perl обновляется DNS? Тогда уж лучше где-то хранить текущий IP и обновлять его только в случае реального изменения, а не раз в пять минут… Нехорошо получается иначе по отношению к яндексу.
                • 0
                  >> Можно немного усложнить Perl-скрипт — к примеру, записывать при каждом обновлении IP-адрес в файл, считывать его в начале скрипта и отправлять запросы Яндексу только в том случае, если он изменился.

                  А на счет роутера — у меня на нем прошивка от Ростелекома, с возможностью просмотра интерактивного ТВ.
                  В сам роутер какие-либо скрипты установить нет возможности.
                  Белого IP нет.
                  К нему подключен Raspberry PI, который я использую в качестве сервера, к нему от роутера проброшена пара портов.
                  Он и выполняет этот скрипт.
                  • 0
                    Добавил в скрипт несколько строк для кэширования IP в файл.
                    Теперь запросы к Яндексу отправляются, только если он изменился.
                  • +1
                    В DNS на яндексе для домена example-site.ru можно прописать CNAME запись на домен example.dlinkddns.com и необходимость в этом скрипте отпадет сама-собой,

                    en.wikipedia.org/wiki/CNAME_record
                    • 0
                      Я пробовал с CNAME записью, у меня получилось прописать ее только для поддомена, например www.example-site.ru.
                      • 0
                        В нормальном DNS сервере домен example-site.ru описывается строкой
                        CNAME example.dlinkddns.com
                        На Яндексе нужно поставить @ вместо пустой строки
                      • 0
                        По той же ссылке, которую вы приводите, написано, почему для example-site.ru нельзя прописать CNAME-запись.
                        Вот DNAME можно, да. Только она про другое (да и не поможет в данном случае — там NS-записи тоже станут совпадать).
                        • 0
                          Где это там такое написано?
                          • 0
                            «An alias defined in a CNAME record must have no other resource records of other types (MX, A, etc.). (RFC 1034 section 3.6.2, RFC 1912 section 2.4) The exception is when DNSSEC is being used, in which case there can be DNSSEC related records such as RRSIG, NSEC, etc. (RFC 2181 section 10.1)»
                            У example-site.ru обязана быть NS-запись. Так что CNAME туда навесить уже нельзя.

                      • +2
                        Рекомендую ознакомиться с freedns.afraid.org — достаточно добавить в свой аккаунт домен (и указать у своего регистратора новые нэйм-сервера), и периодически на любом компе за роутером дергать ссылку (например, wget`ом по крону) типа такой: freedns.afraid.org/dynamic/update.php?blablabla, а сервис сам определит твой IP-адрес, гарантируя работоспособность домена.

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