📜 ⬆️ ⬇️

We make dDNS client for DNS Yandex on MikrotikOS

I recently talked with a friend about DynDNS and similar services, and I remembered that I had long wanted to implement an analog based on the API that Yandex provides to manage DNS hosting. For several years now I have owned a wonderful piece of iron, Mikrotik RB750GL, and I really wanted to update the record for her.
But until recently, this was not possible, since MikroTik can download files only via HTTP, and the Yandex API works only via HTTPS. And having come to Wiki, Mikrotik saw a cherished record:
Fetch now supports HTTPS protocol. By default, it allows you to check the certificate. CRL checking is never done.


The script began to write even when the version of Mikrotik RouterOS was 6.0rc14, and continued on the release version 6.0
Well, now the actual script:

The first part of the script is setting. All the necessary parameters are specified in the body of the script as local variables. This is the domain name, token and record ID. We will get the current IP from the properties of the interface, we specify its name.
The token can only be obtained with pens, the ID can be automated, but I did not need it. You can read in the DNS API documentation:

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

Here 2 global variables, about them later.

The second part of the script is getting the current IP from the interface. In the $ YaDNSCurrentIP variable, we get an IP address, if there is an error somewhere, the script writes an explanation to the log and ends.
 # 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." } 

')
I will a little explain with various "previous" IP. I have them 2:

 :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 "<"] ) ] 


And now about this piece of the script and why it happened:
 :local YaDNSAPI [:resolve "pddimp.yandex.ru"] /tool fetch mode=https address="$YaDNSAPI" host="pddimp.yandex.ru" src-path=$YaDNSsrcpath1 dst-path="/YaDNSGetDomainRecord.txt" 

First, I used the / tool fetch call as follows:
 /tool fetch mode=https address="pddimp.yandex.ru" src-path=$YaDNSsrcpath dst-path="/YaDNS.txt" 

But with this version of the call from the script, the command worked about a quarter of the time and the script was simply interrupted at this place. Long could not why so. I ran this script many times from the console until I realized that Yandex sometimes returns a 404 error, but I still do not understand why. I talked with tech support mikrotik and they guided me to the next thought - first rezolvit IP API, and then contact him by IP. This option has earned me.

And the final part of the script, directly update. In order not to pull Yandex in vain, we will update only if the current IP does not match one of the previous ones. The $ YaDNSForceUpdateOnce variable is left in case it is necessary for the script to work in any case, use it as I understand it, I have a separate script that sets it equal to 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" } 


Cons, from which he could not get rid of:


To use the script, add it to the scheduler, I set the interval to 5 minutes.
/ system script run UpdateYaDNS

Download the full text of the script on PasteBin

UPD
Pastebin
Added local variable
 :local YaDNSsubdomain "xxx.domain.ru" 

As is clear from the name - subdomain. If you need to update the record of the domain itself, then you need to specify this variable equal to $ YaDNSdomain

Source: https://habr.com/ru/post/180497/


All Articles