Делаем dDNS-клиент для DNS Яндекса на MikrotikOS

в 20:51, , рубрики: Dynamic DNS, mikrotik, яндекс, Яндекс API, метки: ,

Зашёл недавно с другом разговор про DynDNS и подобные сервисы, и я вспомнил что давно хотел реализовать аналог на базе API которое предоставляет Yandex для управления DNS-хостингом. Уже несколько лет я владею чудесной железкой Mikrotik RB750GL и очень хотелось чтобы обновляла запись именно она.
Но до недавнего времени это было не возможно, так как MikroTik умеет скачивать файлы только по HTTP, а API Yandex работает только по HTTPS. И вот зайдя на Wiki Mikrotik увидел заветную запись:

Fetch now supports HTTPS protocol. By default no certificate checks are made, but setting check-certificate to yes enables trust chain validation from local certificate store. CRL checking is never done.


Скрипт начал писать ещё тогда когда версия Mikrotik RouterOS была 6.0rc14, а продолжил уже на релизной версии 6.0
Ну а теперь собственно скрипт:

Первая часть скрипта – это настройка. Все необходимые параметры указываем в теле самого скрипта как локальные переменные. Это имя домена, токен и ID записи. Текущий IP будем получать из свойств интерфейса, указываем его имя.
Токен можно получить только ручками, получение ID можно автоматизировать, но мне это не было нужно. Почитать можно в документации API DNS:

:local YaDNSdomain "domain.ru"
:local YaDNStoken "132456789012345678901234567890"
:local YaDNSrecordid "1234567"
:local YaDNSTTL "300"
:local YaDNSInterfaceName "PPPoE_NBN"

:global YaDNSForceUpdateOnce
:global YaDNSPreviousIP

Здесь же 2 глобальные переменные, о них позже.

Вторая часть скрипта – получение текущего IP с интерфейса. В переменной $YaDNSCurrentIP получим IP адрес, если где-то ошибка — скрипт напишет в лог пояснение и завершится.

# get the current IP address from the interface

:if ([:len [/interface find name=$YaDNSInterfaceName]] = 0 ) do={
 :log info "UpdateYaDNS: No interface named $YaDNSInterfaceName , please check configuration."
 :error "UpdateYaDNS: No interface named $YaDNSInterfaceName , please check configuration."
}

:local YaDNSYaDNSCurrentIPMask [ /ip address get [/ip address find interface=$YaDNSInterfaceName] address ]

:local YaDNSCurrentIP [:pick $YaDNSYaDNSCurrentIPMask 0 [:find $YaDNSYaDNSCurrentIPMask "/"]]

:if ([ :typeof $YaDNSCurrentIP ] = "nothing" ) do= {
 :log info "UpdateDynDNS: No ip address present on $YaDNSInterfaceName, please check."
 :error "UpdateDynDNS: No ip address present on $YaDNSInterfaceName, please check."
}

Немного поясню с различными «предыдущими» IP. Их у меня 2:

  • $YaDNSPreviousIP – это IP значение с момента когда скрипт последний раз пытался обновить IP
  • $YaDNSDomainRecord – это значение которое мы спросили у Яндекса через метод get_domain_records

:if ([:typeof $YaDNSPreviousIP] = "nothing" ) do={ :global YaDNSPreviousIP 0.0.0.0 }

:local YaDNSsrcpath1 ( "nsapi/get_domain_records.xml?token=" . $YaDNStoken . "&domain=" . $YaDNSdomain )

:local YaDNSAPI [:resolve "pddimp.yandex.ru"]
/tool fetch mode=https address="$YaDNSAPI" host="pddimp.yandex.ru" src-path=$YaDNSsrcpath1 dst-path="/YaDNSGetDomainRecord.txt"

:local Result1 [/file get YaDNSGetDomainRecord.txt contents]
:local Result2 [:pick $Result1 ([:find $Result1 "id="$YaDNSrecordid"]) ([:find $Result1 "id="$YaDNSrecordid"]+42) ]
:set YaDNSDomainRecord [:pick $Result2 ([:find $Result2 ">"] + 1) ( [:find $Result2 "<"] ) ]

А вот теперь об этом куске скрипта и почему так получилось:

:local YaDNSAPI [:resolve "pddimp.yandex.ru"]
/tool fetch mode=https address="$YaDNSAPI" host="pddimp.yandex.ru" src-path=$YaDNSsrcpath1 dst-path="/YaDNSGetDomainRecord.txt"

Сначала я использовал вызов /tool fetch следующим образом:

/tool fetch mode=https address="pddimp.yandex.ru" src-path=$YaDNSsrcpath dst-path="/YaDNS.txt"

Но при таком варианте вызова из скрипта команда срабатывала примерно в четверти случаев и скрипт просто прерывался на этом месте. Долго не мог почему так. Много раз запускал этот скрипт из консоли пока не понял, что Яндекс иногда возвращает ошибку 404, но почему – так и не понял. Пообщался с техподдержкой микротика и они навели меня на следующую мысль – сначала резолвить IP API, а потом уже обращаться к нему по IP. Такой вариант у меня заработал.

И заключительная часть скрипта, непосредственно обновление. Чтобы не дёргать понапрасну Яндекс обновлять будем только если текущий IP не совпадает с одним из предыдущих. Переменная $YaDNSForceUpdateOnce оставлена на тот случай если надо чтобы скрипт отработал в любом случая, использовать по своему разумению, у меня есть отдельный скрипт, который устанавливает её равной true.

:if (($YaDNSForceUpdateOnce or ($YaDNSCurrentIP != $YaDNSPreviousIP) or ($YaDNSCurrentIP != $YaDNSDomainRecord)) =  true) do={

  :log info "UpdateYaDNS: Try Update"

  :log info "UpdateYaDNS: YaDNSForceUpdateOnce = $YaDNSForceUpdateOnce"
  :log info "UpdateYaDNS: YaDNSPreviousIP = $YaDNSPreviousIP"
  :log info "UpdateYaDNS: YaDNSCurrentIP = $YaDNSCurrentIP"
  :log info "UpdateYaDNS: YaDNSDomainRecord = $YaDNSDomainRecord"

  :local YaDNSsrcpath2 ( "nsapi/edit_a_record.xml?token=" . $YaDNStoken . "&domain=" . $YaDNSdomain . "&record_id=" . $YaDNSrecordid . "&ttl=" . $YaDNSTTL . "&content=" . $YaDNSCurrentIP )

  :local YaDNSAPI [:resolve "pddimp.yandex.ru"]

  /tool fetch mode=https address="$YaDNSAPI" host="pddimp.yandex.ru" src-path=$YaDNSsrcpath2 dst-path="/YaDNS.txt"
  :local result [/file get YaDNS.txt contents]

  :global YaDNSResult [:pick $result ([:find $result "<error>"]+7) [:find $result "</error>"]]

  :if ( $YaDNSResult = "ok" ) do={
   :set YaDNSForceUpdateOnce false
    :set YaDNSPreviousIP $YaDNSCurrentIP
    :log info "UpdateYaDNS: Update Success"
  }

  :log info "UpdateYaDNS: Result: $YaDNSResult"
}

Минусы, от которых так и не смог избавиться:

  • Хранение полученных от Яндеска ответов в файлах. По другому не смог сделать
  • Парсинг XML сделан через пятую точку

Для использования скрипта добавьте его в планировщик, я поставил интервал 5 минут.
/system script run UpdateYaDNS

Скачать полный текст скрипта можно на PasteBin

Автор: Klajnor

Источник

Поделиться

  1. mitroxa:

    а вот тут под винду и вроде как все работает, юзаю сам http://dns-ip.ru/Home/DynDns

* - обязательные к заполнению поля