Navigation Menu

Skip to content

Instantly share code, notes, and snippets.

@v12
Last active April 23, 2021 15:24
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save v12/8892066 to your computer and use it in GitHub Desktop.
Save v12/8892066 to your computer and use it in GitHub Desktop.
RouterOS (MikroTik) Yandex DNS subdomain A record updater.
########
# Name: yandex-ddns
# Author: v12
# URL: https://gist.github.com/v12/8892066
########
### Settings section
# There is no way of getting token automatically via script, so you should get it manually: https://pddimp.yandex.ru/get_token.xml?domain_name=example.com
# More info: http://api.yandex.ru/pdd/doc/reference/api-dns_get_token.xml
:local apiToken "12345678910123456789101234567891012345678910123456789101234567"
# Your domain delegated to Yandex
:local YaDNSdomain "example.com"
# Subdomain which you're going to update (only subdomain, not the full hostname)
# If you want to update A record for subdomain.example.com, you should set this variable to "subdomain"
:local YaDNSsubdomain "subdomain"
:local YaDNSTTL "300"
# Interface which IP address will be used for update
:local interfaceName "ether1-gateway"
:local updateHost "pddimp.yandex.ru"
:local updateHostIp [ :resolve $updateHost ]
:local updateUrl "nsapi/"
:local updateQueryGeneral ( ".xml\?token=" . $apiToken . "&domain=" . $YaDNSdomain )
:local updateQueryGeneralSub ( $updateQueryGeneral . "&subdomain=" . $YaDNSsubdomain )
### Settings section END
:local findSubdomainEntry do={
:local searchOffset 0
:if ([:typeof $1] = "num") do={ :set searchOffset $1 }
:if ([:typeof $1] = "str") do={ :set searchOffset [ :tonum $1 ] }
:local searchEntrySubstr ( "<record domain=\"" . $subdomain . "." . $domain . "\"" )
:local searchEntrySubstrPos [ :find $string $searchEntrySubstr $searchOffset ]
:if ([:typeof $searchEntrySubstrPos] != "num") do={
:return false
}
:return [ :pick $string ($searchEntrySubstrPos + [ :len $searchEntrySubstr ] ) [ :find $string "</record>" $searchEntrySubstrPos ] ]
}
:log info "[Ya.DNS] Attempting to update A record for $YaDNSsubdomain.$YaDNSdomain"
:if ([ :len [ /interface find name=$interfaceName ] ] = 0 ) do={
:log warning "[Ya.DNS] No interface '$interfaceName' was found, please do check updater script configuration."
:error "[Ya.DNS] No interface '$interfaceName' was found, please do check updater script configuration."
}
# Get current IP address for interface
:local currentInterfaceIp [ :tostr [ /ip address get [/ip address find interface=$interfaceName] address ] ]
:set currentInterfaceIp [ :pick $currentInterfaceIp 0 [ :find $currentInterfaceIp "/" ] ]
:if ([ :len $currentInterfaceIp ] = 0 ) do= {
:log info "[Ya.DNS] No IP address is assigned to the interface '$interfaceName'."
:error "[Ya.DNS] No IP address is assigned to the interface '$interfaceName'."
}
/tool fetch mode=https address=$updateHostIp host=$updateHost src-path=( $updateUrl . "get_domain_records" . $updateQueryGeneral ) dst-path="YaDNS.txt"
:local result [/file get YaDNS.txt contents]
# Checking whether request is successful or not
:local error [:pick $result ([:find $result "<error>"]+7) [:find $result "</error>"]]
:if ($error != "ok") do={
:log warning "[Ya.DNS] Unable to get list of records for $YaDNSdomain ($error)"
:error "[Ya.DNS] Unable to get list of records for $YaDNSdomain ($error)"
}
# Searching an entry with the subdomain
:local aRecordEntry
:local doneSearchingEntry false
:local entrySearchLastResult
while (!$doneSearchingEntry) do={
:set entrySearchLastResult [ $findSubdomainEntry subdomain=$YaDNSsubdomain domain=$YaDNSdomain string=$result ([ :find $result $entrySearchLastResult ] + 1) ]
:if ([:typeof $entrySearchLastResult] = "str") do={
:local currentEntryTypeSearch "type=\""
:local currentEntryTypePos ([ :find $entrySearchLastResult $currentEntryTypeSearch ] + [ :len $currentEntryTypeSearch ])
:if ([ :pick $entrySearchLastResult $currentEntryTypePos [ :find $entrySearchLastResult "\"" $currentEntryTypePos ] ] = "A") do={
:set aRecordEntry $entrySearchLastResult
:set doneSearchingEntry true
}
} else={
:set doneSearchingEntry true
}
}
:local queryParams ( "&content=" . $currentInterfaceIp . "&ttl=" . $YaDNSTTL . "&subdomain=" . $YaDNSsubdomain )
:local queryAction "add_a_record"
if ($doneSearchingEntry && ([ :typeof $aRecordEntry ] != "str")) do={
# Create new entry
:log info "[Ya.DNS] Not found A entry for $YaDNSsubdomain.$YaDNSdomain. Attempting to create one."
:put "[Ya.DNS] Not found A entry for $YaDNSsubdomain.$YaDNSdomain. Attempting to create one."
} else={
# Get the IP that is already assigned to the existing record
:local currentRecordIp [ :toip [ :pick $aRecordEntry ([ :find $aRecordEntry ">" ] + 1) [ :len $aRecordEntry ] ] ]
# Compare current IP on our interface with the IP in the record
:if ($currentRecordIp = $currentInterfaceIp) do={
:log info "[Ya.DNS] IP address hasn't changed since last update. Terminating."
:error "[Ya.DNS] IP address hasn't changed since last update. Terminating."
} else={
# Searching id of entry we found
:local recordIdSubstr "id=\""
:local recordIdSubstrStart ( [ :find $aRecordEntry $recordIdSubstr ] + [ :len $recordIdSubstr ] )
:local recordId [ :pick $aRecordEntry $recordIdSubstrStart [ :find $aRecordEntry "\"" $recordIdSubstrStart ] ]
:set queryAction "edit_a_record"
:set queryParams ( $queryParams . "&record_id=" . $recordId )
}
}
/tool fetch \
mode=https \
address=$updateHostIp \
host=$updateHost \
src-path=( $updateUrl . $queryAction . $updateQueryGeneral . $queryParams ) \
dst-path="YaDNS_2.txt"
:set result [/file get YaDNS_2.txt contents]
# Avoiding "no such item" bug: fetch result needs some time to be saved
:delay 1000ms
/file remove "YaDNS.txt"
/file remove "YaDNS_2.txt"
# Checking whether request is successful or not
:set error [:pick $result ([:find $result "<error>"]+7) [:find $result "</error>"]]
:if ($error != "ok") do={
:log warning "[Ya.DNS] Unable to update record for $YaDNSsubdomain.$YaDNSdomain ($error)"
:error "[Ya.DNS] Unable to update record for $YaDNSsubdomain.$YaDNSdomain ($error)"
}
:log info "[Ya.DNS] Successfuly updated A record for $YaDNSsubdomain.$YaDNSdomain ($currentInterfaceIp)"
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment