We make dDNS client for Yandex DNS on MikrotikOS

    Recently I went into a conversation 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 for managing DNS hosting. For several years now I have owned a wonderful Mikrotik RB750GL piece of hardware and I really wanted it to be the one that updated the record.
    But until recently, this was not possible, since MikroTik can only download files via HTTP, and the Yandex API only works via HTTPS. And then going to Wiki Mikrotik saw the coveted entry:
    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.


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

    The first part of the script is the setup. We indicate all the necessary parameters in the body of the script itself as local variables. This is the domain name, token and record ID. We will receive the current IP from the properties of the interface, specify its name.
    The token can be obtained only by handles, the receipt of 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
    

    There are 2 global variables, more about them later.

    The second part of the script is getting the current IP from the interface. In the variable $ YaDNSCurrentIP we get the IP address, if somewhere there is an error, the script will write an explanation to the log and end.
    # 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’ll explain a little with the various “previous” IPs. I have 2 of them:
    • $ YaDNSPreviousIP is the IP value since the last time the script tried to update IP
    • $ YaDNSDomainRecord is the value we asked Yandex through the get_domain_records method

    :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 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 option of calling from a script, the command worked in about a quarter of cases and the script simply interrupted at this point. For a long time I could not why. I ran this script many times from the console until I realized that Yandex sometimes returns a 404 error, but why I did not understand. I talked with Mikrotik technical support and they led me to the following thought - first resolve the IP API, and then access it via IP. This option worked for me.

    And the final part of the script, the update itself. In order not to pull in vain, Yandex will be updated only if the current IP does not match one of the previous ones. The variable $ YaDNSForceUpdateOnce is left in case you need the script to work in any case, use it as you see fit, I have a separate script that sets it 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 ""]+7) [:find $result ""]]
      :if ( $YaDNSResult = "ok" ) do={
       :set YaDNSForceUpdateOnce false
        :set YaDNSPreviousIP $YaDNSCurrentIP
        :log info "UpdateYaDNS: Update Success"
      }
      :log info "UpdateYaDNS: Result: $YaDNSResult"
    }
    


    Cons that I couldn’t get rid of:
    • Storage of the answers received from Yandex in files. I could not do otherwise
    • XML parsing done through fifth point


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

    You can download the full script on PasteBin

    UPD
    PasteBin
    A local variable has been added
    :local YaDNSsubdomain "xxx.domain.ru"
    

    As the name implies, it’s a subdomain. If you need to update the record of the domain itself, then you need to specify this variable equal to $ YaDNSdomain

    Also popular now: