Sync Address List with DNS Cache - MikroTik Script RouterOS

This script will update an address list from a given DNS cache search. This address list can then be used for example in Firewall settings.

# Sync an address list with DNS cache
# Version: 1.0.0
# This script currently only works with type A and non-static DNS records.
# If no matching DNS records are found:
# the address list is removed.
# If matching DNS records are found:
# if an address list entry is NOT found in search result, it is removed.
# if an address list entry IS found in search result, is not modified.
# if a search result is NOT found in address list, it is added.
#
# Search string format:
# "(:|=|~)"
# :   match string anywhere
# =  match string exactly (case sensitive)
# ~  match string using regular expression (requires ROS >= v3.23)
#
# Example:  To search all records containing 'google.com':
# :local search ":google.com"
#
# To search all records ending with 'microsoft.com':
# :local search "~microsoft\\.com\$"
:local search ":google.com"
# The prefix of address list ("" = none)
:local listprefix "DNSCache_"
# Internal processing...
:local IPs ""
:local Names ""
:local sop [:pick [:tostr $search] 0 1]
:set search [:pick [:tostr $search] 1 [:len [:tostr $search]]]
/ip dns cache all {
   :local findex; :local property; :local value; :local name; :local type; :local data
   :local sfound 0
   :foreach rule in=[print detail as-value where static=no] do={
      :set name ""; :set type ""; :set data ""

      :foreach item in=$rule do={
         :set findex [:find [:tostr $item] "="]
         :set property [:pick [:tostr $item] 0 $findex]
         :set value [:pick [:tostr $item] ($findex + 1) [:len [:tostr $item]]]

         :if ($property = "name") do={ :set name $value }
         :if ($property = "type") do={ :set type $value }
         :if ($property = "data") do={ :set data $value }
      }
# Search DNS cache name using specified operator
      :set sfound 0
      :if ($sop = ":") do={
         :if ([:find [:tostr $name] [:tostr $search]] != "" && $type = "A") do={ :set sfound 1 }
      }
      :if ($sop = "=") do={
         :if ([:tostr $name] = [:tostr $search] && $type = "A") do={ :set sfound 1 }
      }
      :if ($sop = "~") do={
         :if ([:tostr $name] ~ [:tostr $search] && $type = "A") do={ :set sfound 1 }
      }
      :if ($sfound = 1) do={
#         :put ("Found " . $name . " -> " . $data)
         :set IPs ($IPs . $data . ",")
         :set Names ($Names . $name . ",")
      }
   }
# /ip dns cache all
}
:put ("DNS cache search found " . [:len [:toarray $IPs]] . " match(es) for '" . $search . "'")
# Search through IPs and add to address list
/ip firewall address-list {
   :local findex; :local listaddr; :local IPsFound ""

   :put ("Searching address list '" . ($listprefix . [:tostr $search]) . "'...")
   :foreach l in=[find list=($listprefix . [:tostr $search])] do={
      :set listaddr [get $l address]
      :if ([:len [:find [:toarray $IPs] [:toip $listaddr]]] = 0) do={
         :put ("   " . $listaddr . " not found in search, removing...")
         remove $l
      } else={
#         :put ($listaddr . " found address in IPs")
         :set IPsFound ($IPsFound . $listaddr . ",")
      }
   }
# Add remaining records to address list
   :set findex 0
   :foreach ip in=[:toarray $IPs] do={
      :if ([:len [:find [:toarray $IPsFound] [:toip $ip]]] = 0) do={
         :put ("   Adding address " . $ip)
         add list=($listprefix . $search) address=[:toip $ip] comment=([:pick [:toarray $Names] $findex]) disabled=no
      }
      :set findex ($findex + 1)
   }
# /ip firewall address-list
}