Just a quick update on the topic. I have replaced my script with a version that registers the name into an authoritative DNS, and also the local cache, with ttl=1min. I have also changed it so when the lease times out, then the registration is not changed at all.
This way:
* When there is no internet connection, the names can still be resolved using the local cache. So for example, if the internet connection is down and a samba server is rebooted, then the FQDN of the server cannot be registered into the authoritative DNS server, but it will be registered in the local cache. As a result, local clients will be able to access it with its FQDN.
* When there is internet connection, then everything is honky dory, the FQDN can be used from any network, across different VPNs etc. and there is no need to use split DNS at all.
I'm not very good at MikroTik scripting, and I'm not very proud of my script. I'm 100% sure that anyone can write a better script, but just in case it might help.
This script below does the heavy work. It extracts the hostname from the DHCP request, converts to lower case, and appends a subdomain and a zone. (My experience is that misconfigured windows clients will send garbage in the domain part, and also they will be sending mixed lower and upper case characters.)
This script has no literal values burned it. It is general, and can be used for any domain or hostname.
# param: name - "example-hostname.misconfigured.domain.hu" only the example-hostname will be used for registering "example-hostname.office.example.com"
# param: subdomain - "office" the real subdomain that will be used (e.g. the "office" from office.example.com)
# param: zone - "example.com" -> name of the zone to be updated (e.g. the "example.com" from "office.example.com")
# param: ip - 192.168.14.112 -> ip address of the host
# param: ttl - 60 -> ttl of the host
# param keyName - "ddns-key.example.com" -> ddns key for the zone
# param key - "**hmac-md5***==" -> ddns key
# param ns - "ns1.example.com" -> FQDN of the NS server to send the request to
:local lowerCaseUntilFirstDot do={
# param: entry
:local lower "abcdefghijklmnopqrstuvwxyz";
:local upper "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
:local result "";
:local ignorerest false;
:for i from=0 to=([:len $entry] - 1) do={
:local char [:pick $entry $i];
if ($char=".") do={ :set ignorerest true; };
if ( ! $ignorerest ) do={
:local pos [:find $upper $char];
:if ($pos > -1) do={:set char [:pick $lower $pos]};
:set result ($result . $char);
}
}
:return $result;
}
:local hostname [$lowerCaseUntilFirstDot entry=$name]
:local subhostname ($hostname .".".$subdomain);
:local fullhostname ($subhostname .".".$zone);
:put ("Processing ". $fullhostname. " with ns=".$ns);
# First, we update the local DNS cache. This makes sure
# that the host is available with its FQDN even if the internet
# connection is down.
/ip/dns/static
remove [find where name=$fullhostname]
add name=$fullhostname address=$ip ttl=60 comment=DHCP
:local dnssrv [/resolve domain-name=$ns];
:put ("NS server $ns has address $dnssrv");
# see https://wiki.mikrotik.com/wiki/Manual:Scripting#Catch_run-time_errors
:local oldIP 0.0.0.0
:do {
:set oldIP [/resolve server=$dnssrv domain-name=$fullhostname];
} on-error={
:set oldIP 0.0.0.0;
/log info message= ("updateSubDomain: " . $fullhostname . " is a new host");
};
:put "oldIP = $oldIP";
:put "newIp = $ip";
:if ($ip != $oldIP) do={
:local message ("updateSubDomain: changing IP address of $fullhostname from $oldIP to $ip, ttl=".$ttl);
:put $message;
/log info message=$message;
# debug-hoz
#:local message ("/tool dns-update name=$subhostname address=$ip dns-server=$dnssrv zone=$zone key-name=$keyName key=$key ttl=$ttl;");
#:put $message;
/tool dns-update name=$subhostname address=$ip dns-server=$dnssrv zone=$zone key-name=$keyName key=$key ttl=$ttl;
} else={
:local message ("updateSubDomain: IP address of $fullhostname unchanged ($oldIP)");
:put $message;
/log info message=$message;
}
That script below just pre-fills some parameters. This is just a wrapper that makes it easy to send updates that are tied to "the subdomain that is associated with my router".
#param name "example-hostname.bad.host.name"
#param ip 192.168.14.100
:local cmd [:parse [:system script get updateSubDomain source]]
$cmd name=$name subdomain="office" zone="example.com" ip=$ip ttl=60 keyName="ddns-key.example.com" key="GFGZacs6329N1GbSdOcnKQ==" ns="ns.example.com"
And finally, the on lease script, it is also general
:log info "#1"
# for parameters, see https://help.mikrotik.com/docs/display/ROS/DHCP#DHCP-Leases
# and the lease-script property
:if ( [ :len $leaseActIP ] <= 0 ) do={ :error "empty lease address" }
:log info "#2 bound=$leaseBound ip=$leaseActIP"
:if ( $leaseBound = 1 ) do=\
{
/ip/dhcp-server/lease
:local leaseId [ find address=$leaseActIP ]
:log info "#3 leaseId=$leaseId"
# Check for multiple active leases for the same IP address. It's weird and it shouldn't be, but just in case.
:if ( [ :len $leaseId ] != 1) do=\
{
:log info "onDhcpLease: not registering domain name for address $leaseActIP because of multiple active leases for $leaseActIP";
:error "multiple active leases for $leaseActIP";
}
#:local hostname [ get $leaseId host-name ]
:local hostname $"lease-hostname"
:log info "#4 hostname=$hostname"
:if ([:len $hostname]=0) do={
:log info "onDhcpLease: not registering domain name for address $leaseActIP because client did not specify a host name";
} else {
:log info "onDhcpLease: registering domain name $hostname for address $leaseActIP";
:local nsupdate [:parse [:system script get onUpdateMySubDomain source]]
$nsupdate name=$hostname ip=$leaseActIP
}
}