DDNS на nic.ru с поддержкой Multi-Wan

Здесь выкладываем скрипты
Правила форума
Уважаемые Пользователи форума, обратите внимание!
Ни при каких обстоятельствах, Администрация форума, не несёт ответственности за какой-либо, прямой или косвенный, ущерб причиненный в результате использования материалов, взятых на этом Сайте или на любом другом сайте, на который имеется гиперссылка с данного Сайта. Возникновение неисправностей, потерю программ или данных в Ваших устройствах, даже если Администрация будет явно поставлена в известность о возможности такого ущерба.
Просим Вас быть предельно осторожными и внимательными, в использовании материалов раздела. Учитывать не только Ваши пожелания, но и границы возможностей вашего оборудования.
Ответить
Frozer
Сообщения: 49
Зарегистрирован: 01 апр 2013, 13:35

Для пользователей услуги ДНС-хостинг на nic.ru

Скрипт может работать и с одним WAN, но главное его преимущество, что работать может с несколькими, при этом для доменов, которым назначается IP адрес шлюза по-умолчанию, можно делать проверку на серый IP (можно и не делать), а для доменов, которые привязаны к определенным WAN, IP адрес сверяется только с фактическим IP адресом интерфейса). Для определенных доменов, привязанных к конкретному WAN, можно сделать "переадресацию" на шлюз по-умолчанию, если этот конкретный WAN перестанет работать.

Все блоки прокомментированы, правда на английском, если что не понятно - спрашивайте или пишите на мыло/инстаграм из комментария.

Код: Выделить всё

# DDNS_NIC_ru_MultiWAN Script v.2.0.0 (12.11.2021)
# Author: Sergey Krivosheyev
# About bugs, questions & feedback please contact to: sergey@krivosheyev.ru, instagram: wireless_sensei

# Minumum ROS version supported is 6.43 (7.x untesed)!

# Please enable debug logging for scripts for diagnostics.
:local StDomainsAr [:toarray ""];

# **************************************** SETUP SETTINGS BELOW ******************************************

# WAN INTERFACES FOR DUAL NAT CHECK (NAMES, COMMA SEPARATED)
# If interfaces are specified here, we will compare their IP address with the IP address received from an external service
# and assign it to domains only if it matches the IP address of one of the interfaces or with the whitelist (below).
# Also you can leave empty value ("") if dual NAT check not needed.
:local StWANsAr "";

# IP WHITE-LIST FOR DUAL WAN CHECK (IPs or NETs, COMMA SEPARATED)
# When checking for dual NAT, the addresses of the interfaces specified above (StWANsAr parameter) are automatically included
# to the white list. But in addition, you can specify another IP addresses (or network/mask) - if IP address received from an
# external service matches one of them, it is considered as correct regardless of whether it is assigned to any of the WANs or not.
# Parameter ignored if dual NAT check disabled (StWANsAr parameter set to "").
:local StWhiteListAr "";

# ADREESS LIST WITH PING IP's (NAME)
# You can create an address list in the firewall section and specify some IPs for ping there, so we can additionally check
# the availability of Enternet via each of WAN interface. If the parameter is not specified (""), then the WANs is checked
# only by the status of its IP-address in the interfaces section. At least 3 IPs must be in that address-list.
:local StPingAddrList "";

# HOW MUCH PING REQUESTS TO SEND FOR ONE HOST (4-10)
# Parameter ignored if $StPingAddrList not set.
:local StPingCount 4;

# PING QUALITY REQUIREMENTS (50-100%)
# Parameter ignored if $StPingAddrList not set.
:local StPingQualityReq 100;

# DNS SERVERS BLACK-LIST
# If there is a local DNS server that has static records for the same domains that are managed by this script and
# this server, under certain conditions, may be specified as one of the DNS servers for this router, then it is better to
# add local server to the black-list (to prevent conflicts).
:local StDNSServBlListAr "";

# DOMAINS WITH DINAMIC DNS-NAMES (COMMA SEPARATED, LOWERCASE) >>>>>>>>>>>>>>>>>>>>>>>>
# Domains that are assigned the external IP address of the interface for which is a default route exists.
:set ($StDomainsAr->"default") "DOMAIN1_LINKED_TO_DEFAULT_WAN,DOMAIN2_LINKED_TO_DEFAULT_WAN,ETC...";

# Also you can perform an IP check for domains, which should be assigned the IP address of a specific WAN interface.
# It is possible to use check for each of the WANs separately (add more lines equivalent to the line below).
# Note: it is NOT necessary to add the names of the interfaces used here to the StWANsAr parameter.
#:set ($StDomainsAr->"WAN1_NAME") "DOMAIN1_LINKED_TO_WAN1,DOMAIN2_LINKED_TO_WAN1,ETC...";

# Also you can switch some domains linked to specific WANs to use external IP of default WAN if "first" is not working.
:set ($StDomainsAr->"to_default") "DOMAIN1_LINKED_TO_SOME_WAN,DOMAIN2_LINKED_TO_SOME_WAN,ETC...";
# <<<<<<<<<<<<<<<<<<<<<<<<

# LOGIN & PASSWORD FOR NIC.RU API SERVICE
:local StUser "";
:local StPass "";

# PLEASE SET ACTUAL (REAL) SCRIPT NAME (IT IS FOR SIMULTANIOUS RUN PREVENTON CHECK)
:local StCurrentScriptName "DDNS";

# SCRIPT TIMEOUT (MINUTES)
# The time interval after which the script can be considered as hunged. If the script is run again and running copy
# exists already but the time interval is exceeded, it will remove the hunged copy and run normally
# (otherwise it will not take any additional action).
:local StScriptTimeOut 5;

# PREFIX FOR LOG MESSAGES
:local LP ("DDNS script: ");

# ****************************************** NO SETTINGS BELOW ******************************************

:local t;
:local tAr;
:local c;
:local Count;
:local Counter;
:local tmp;
:local temp;
:local tempAr;
:local Found;
:local Error;
:global DDNSScriptBuffer;

# CHECK FOR SCRIPT'S COPY RUNNING
:local ScrStartMin;
:if ([:tostr [/system script find name=$StCurrentScriptName]] = "") do={
	:log warning ($LP . "Launch terminated, no script found with name of \"" . $StCurrentScriptName . "\".");
	:error ("Launch terminated, no script found with name of \"" . $StCurrentScriptName . "\".");
}
:set tempAr [/system script job find script=$StCurrentScriptName];
:set Counter [:len $tempAr];
:if ($Counter > 1) do={
	:for i from=1 to=($Counter-1) step=1 do={
	:set temp [/system script job get ($tempAr->$i) started];
		:set ScrStartMin {"MinStarted"=([:tonum [:pick $temp 12 14]] * 60 + [:tonum [:pick $temp 15 17]])};
		:set temp [/system clock get time];
		:set ($ScrStartMin->"MinCurrent") ([:tonum [:pick $temp 0 2]] * 60 + [:tonum [:pick $temp 3 5]]);
		:set ScrStartMin (($ScrStartMin->"MinCurrent") - ($ScrStartMin->"MinStarted"));
		:if ($ScrStartMin < 0 || $ScrStartMin >= $StScriptTimeOut) do={
			:do { /system script job remove ($tempAr->$i); } on-error {}
			:log warning ($LP . "A copy #" . $i . " removed by timeout.");
		}
	}
:delay 2
	:if ([:len [/system script job find script=$StCurrentScriptName]] > 1) do={
	:log info ($LP . "Launch terminated - still running another copy.");
	:error "Launch terminated - still running another copy.";
	}
}

# FUNCTIONS >>>
# CHECK SYNTAX FUNCTION (ARG: CheckStr, LP, ParamName)
:global DDNSCheckSyntaxFunc do={
	:if (([:typeof $2] != "str") || ($2 = "")) do={ :set $2 "DDNSCheckSyntaxFunc: "; }
	:if ([:typeof $1] != "str") do={
		:log error ($2 . "Missing or incorrect argument \"CheckStr\".");
		:return false;
	}
	:local ParamName [:toarray ""];
	:set ($ParamName->"DOM") "Domain name";
	:set ($ParamName->"INT") "Interface name";
	:local CharsAllowed [:toarray ""];
	:set ($CharsAllowed->"DOM") "qwertyuiopasdfghjklzxcvbnm012345678-.";
	:set ($CharsAllowed->"INT") "QWERTYUIOPASDFGHJKLZXCVBNMqwertyuiopasdfghjklzxcvbnm0123456789<>_-.";
	:if (([:typeof $3] != "str") || ([:typeof ($ParamName->$3)] != "str")) do={
		:log error ($2 . "Missing or incorrect argument \"ParamName\".");
		:return false;
	}
	:local error false;
	:local t "";
	:local temp "";
	:for i from=0 to=([:len $1] - 1) do={
		:set t [:pick $1 $i];
		:if ([:typeof [:find ($CharsAllowed->$3) $t]] != "num") do={
			:set error true;
			:set t "%";
		}
		:set temp ($temp . $t);
	}
	:if ($error = true) do={
		:log warning ($2 . ($ParamName->$3) . " \"" . $temp . "\" contains incorrect symbols (replaced to \"%\").");
	} else {
		:return true;
	}
}

# STRING TO IP-PREFIX FUNCTION (ARG: Str, LP)
:global DDNSStrToIPPrefFunc do={
	:global DDNSScriptBuffer;
	:if (([:typeof $2] != "str") || ($2 = "")) do={ :set $2 "DDNSStrToIPPrefFunc: "; }
	:if ([:typeof $1] = "nothing") do={
		:log debug ($2 . "Missing argument \$1.");
		:return false;
	}
	:do {
		:set $1 [:tostr $1];
	} on-error {
		:log debug ($2 . "Incorrect argument \$1 or data conversion error.");
		:return false;
	}
	:local temp [:parse (":global DDNSScriptBuffer " . $1)];
	$temp;
	:if (([:typeof $DDNSScriptBuffer] = "ip-prefix") || ([:typeof $DDNSScriptBuffer] = "ip")) do={
		:return $DDNSScriptBuffer;
	} else {
		:return false;
	}
}
# CHECK IF ARG IS IP & HIS TYPE (ARG: Str/IP, LP)
:global DDNSCheckIPTypeFunc do={
	:local Str;
	:if (([:typeof $2] != "str") || ($2 = "")) do={ :set $2 "DDNSCheckIPTypeFunc: "; }
	:do {
		:set Str [:tostr $1];
		:set $1 [:toip $1];
	} on-error {
		:log debug ($2 . "Incorrect argument \$1 or data conversion error.");
		:return false;
	}
	:if ($Str != [:tostr $1]) do={ :return false; }
	:if (($1 in 10.0.0.0/8) || ($1 in 192.168.0.0/16) || ($1 in 127.0.0.0/8)) do={
		:return "LOC";
	} else {
		:return "EXT";
	}
}
# REPLACE CHARS FUNCTION (ARG: StrSearch StrReplaceFrom StrReplaceTo)
:global DDNSReplCharsFunc do={
	:if (([:typeof $1] != "str") || ([:typeof $2] != "str") || ([:typeof $3] != "str")) do={ return false; }
	:local t "";
	:local temp "";
	:for i from=0 to=([:len $1] - 1) do={
	:set t [:pick $1 $i]
		:if ($t = $2) do={
		:set t $3;
		}
	:set temp ($temp.$t);
	}
	return $temp;
}
# <<< FUNCTIONS

# CHECKING PING ADDRESS LIST
:local PingIPs [:toarray ""];
:set Error false;
:set StPingAddrList [:tostr $StPingAddrList];
:if ([:tostr $StPingAddrList] != "") do={
	:set StPingCount [:tonum $StPingCount];
	:set StPingQualityReq [:tonum $StPingQualityReq];
	:if (($StPingCount < 4) || ($StPingCount > 10)) do={
		:set Error true;
		:log warning ($LP . "Incorrect parameter for \$StPingCount set (must be a number from 4 to 10), so only simple WAN's check is available.");
	}
	:if (($StPingQualityReq < 50) || ($StPingQualityReq > 100)) do={
		:set Error true;
		:log warning ($LP . "Incorrect parameter for \$StPingCount set (must be a number from 50 to 100), so only simple WAN's check is available.");
	}
	:if ($Error = false) do={
		:set tempAr [:toarray [/ip firewall address-list find list=$StPingAddrList]];
		:set Counter [:len $tempAr];
		:if ($Counter > 0) do={
			:foreach val in=$tempAr do={
				:set temp [:tostr [/ip firewall address-list get $val address]];
				:set c [:find $temp "/"];
				:if ([:typeof $c] = "num") do={ :set $temp [:pick $temp 0 $c]; }
				:if (([$DDNSCheckIPTypeFunc $temp $LP] != "EXT") || ([typeof [:find [:tostr $temp] "/"]] = "num")) do={
					:log warning ($LP . "Incorrect IP for ping (" . [:tostr $temp] . ") found in address list, ignoring it...");
				} else {
					:set PingIPs ($PingIPs , [:toip $temp]);
				}
			}
			:if ([:len $PingIPs] < 3) do={
				:log warning ($LP . "Too little (<3) external IP's available at \"" . $StPingAddrList . "\" address-list, so only simple WAN's check is available.");
			}
		} else {
			:log warning ($LP . "Address list specified in \$StPingAddrList not found, so only simple WAN's check is available.");
		}
	}
} else {
	:log debug ($LP . "Parameter for \$StPingAddrList not specified, so only simple WAN's check is available.");
}

# CHECKING WANS
:local WANsAllAr [:toarray ""];
:local WANsActiveAr [:toarray ""];
:local WANsIPsAr [:toarray ""];
:local WhiteListAr [:toarray ""];
:set StWANsAr [:toarray $StWANsAr];
:if ([:len $StWANsAr] > 0) do={
	:foreach val in=$StWANsAr do={
		:if ([$DDNSCheckSyntaxFunc $val $LP "INT"] = true) do={
			:if ([:typeof [:find $WANsAllAr $val]] != "num") do={ :set $WANsAllAr ($WANsAllAr, $val); }
		}
	}
}
:if (([:typeof $StDomainsAr] = "array") && ([:len $StDomainsAr] > 0)) do={
	:foreach key,val in=$StDomainsAr do={
		:if (($key != "default") && ($key != "to_default")) do={
			:if ([$DDNSCheckSyntaxFunc $key $LP "INT"] = true) do={
				:if ([:typeof [:find $WANsAllAr $key]] != "num") do={ :set $WANsAllAr ($WANsAllAr, $key); }
			}
		}
	}
}
:if ([:len $WANsAllAr] > 0) do={
	:foreach val in=$WANsAllAr do={
		:do {
			:set t [/ip address find interface=$val disabled=no invalid=no];
			:if ([:tostr $t] != "") do={
				:do {
					:set t [:tostr [/ip address get ([:toarray $t]->0) address]];
					:set c [:find $t "/"];
					:if ([:typeof $c] = "num") do={ :set $t [:pick $t 0 $c]; }
					:if ([$DDNSCheckIPTypeFunc $t $LP] = "EXT") do={
						:set ($WANsIPsAr->$val) $t;
						:set WhiteListAr ($WhiteListAr, [:toip $t]);
						:if ([:len $PingIPs] > 2) do={
							:log debug ($LP . "Performing full \"" . $val . "\" interface check (IPs for ping enought).");
							:set Counter 0;
							:set Found false;
							:while (($Found = false) && ($Counter < [:len $PingIPs])) do={
								:do {
									:set temp [/ping ($PingIPs->$Counter) count=$StPingCount src-address=[:toip $t] interface=$val];
								} on-error {
									:set temp false;
								}
								:if (([:typeof $temp] = "num") && ($temp > 0)) do={
									:set tmp (100 - ($StPingCount-$temp) / $temp * 100);
									:if ($tmp >= $StPingQualityReq) do={
										:set WANsActiveAr ($WANsActiveAr, $val);
										:log debug ($LP . "Full \"" . $val . "\" interface check completed succesfuly (ping quality is " . $tmp . "%).");
										:set Found true;
									}
								}
								:set Counter ($Counter + 1);
							}
							:if ($Found = false) do={
								:log warning ($LP . "Internet via \"" . $val . "\" interface is not available (checked by ping).");
							}
						} else {
							:set WANsActiveAr ($WANsActiveAr, $val);
						}
					} else {
						:log warning ($LP . "IP for \"" . $val . "\" interface (" . $t . ") is incorrect (from private network).");
					}
				} on-error {
					:log warning ($LP . "Can't get IP for \"" . $val . "\" interface.");
				}
			} else {
				:log warning ($LP . "IP for \"" . $val . "\" interface not found.");
			}
		} on-error {
			:log warning ($LP . "Can't find \"" . $val . "\" interface.");
		}
	}
	# CHECKING WHITE-LIST
	:if ([:tostr $StWhiteListAr] != "") do={
		:set StWhiteListAr [:toarray $StWhiteListAr];
		:foreach key,val in=$StWhiteListAr do={
			:set val [$DDNSStrToIPPrefFunc $val $LP];
			:if ($val != false) do={
				:if (($val in 10.0.0.0/8) || ($val in 192.168.0.0/16) || ($val in 127.0.0.0/8)) do={
					:log warning ($LP . "IP for \$StWhiteListAr (" . $val . ") is incorrect (from private network).");
				} else {
					:set WhiteListAr ($WhiteListAr, $val);
				}
			} else {
				:log warning ($LP . "Value #" . ($key + 1) . " for \$StWhiteListAr incorrect (not an IP or network).");
			}
		}
	}
} else {
	:log debug ($LP . "Dual WAN check not used.");
}

# CHECKING DUAL NAT SETTINGS
:if (([:len $StWANsAr] > 0) && ([:len $WANsActiveAr]) = 0) do={
	:log error ($LP . "Can't check for dual NAT (no IP for specified WAN(s) found), terminating...");
	:error "Can't check for dual NAT (no IP for specified WAN(s) found), terminating...";
}

# CHECKING DOMAINS W/DINAMIC DNS
:local DomainsDefAr [:toarray ""];
:local DomainsWANsAr [:toarray ""];
:local DomainsToDefAr [:toarray ""];
:if ([:typeof $StDomainsAr] = "array") do={
	:set tempAr [:toarray ""];
	:foreach key,val in=$StDomainsAr do={
		:if (($key = "default") || ($key = "to_default") || ([:typeof [:find $WANsAllAr $key]] = "num")) do={
			:if (([:typeof $val] = "str") && ($val != "")) do={
				:set val [:toarray $val];
				:foreach v in=$val do={
					:if ([$DDNSCheckSyntaxFunc $v $LP "DOM"] = true) do={
						:if ($key = "default") do={
							:if ([:typeof ($tempAr->$v)] != "str") do={
								:set ($tempAr->$v) "";
								:set DomainsDefAr ($DomainsDefAr, $v);
							} else {
								:log warning ($LP . "Domain \"" . $v . "\" from (\$StDomainsAr->\"default\") parameter set for several WANs or at least twice.");
							}
						} else {
							:if ($key = "to_default") do={
								:if ([:typeof [:find $DomainsToDefAr $v]] != "num") do={
									:set DomainsToDefAr ($DomainsToDefAr, $v);
								} else {
									:log warning ($LP . "Domain \"" . $v . "\" exist in (\$StDomainsAr->\"to_default\") parameter at least twice.");
								}
							} else {
								:if ([:typeof ($tempAr->$v)] != "str") do={
									:set ($tempAr->$v) "";
									:if ([:typeof ($DomainsWANsAr->$key)] = "str") do={ :set ($DomainsWANsAr->$key) (($DomainsWANsAr->$key) . ",") }
									:set ($DomainsWANsAr->$key) (($DomainsWANsAr->$key) . $v);
								} else {
									:log warning ($LP . "Domain \"" . $v . "\" from (\$StDomainsAr->\"" . $key . "\") parameter set for several WANs or at least twice.");
								}
							}
						}
					}
				}
			}
		} else {
			:log warning ($LP . "WAN interface \"" . $key . "\" invalid, so ignoring (\$StDomainsAr->\"" . $key . "\") parameter now...");
		}
	}
} else {
	:log error ($LP . "Incorrect settings for \$StDomainsAr parameter (not an array), terminating...");
	:error "Incorrect settings of \$StDomainsAr (not an array), terminating...";
}

:if (([:len $DomainsDefAr] = 0) && ([:len $DomainsWANsAr] = 0)) do={
	:log error ($LP . "Incorrect settings for \$StDomainsAr parameter (no domains found), terminating...");
	:error "Incorrect settings for \$StDomainsAr parameter (no domains found), terminating...";
}

# TRYING TO GET EXTERNAL IP
:local DefExternalIP "";
:if (([:len $DomainsDefAr] > 0) || ([:len $DomainsToDefAr] > 0)) do={
	:log debug ($LP . "Trying to get external IP of active default WAN.");
	:set t "";
	:set Counter 0;
	:while (($DefExternalIP = "") && ($Counter < 5)) do={
		:if ($Counter > 0) do={ :delay 5; }
		:do {
			:log debug ($LP . $t . "Try number " . ($Counter + 1) . " of 5...");
			:set tempAr [/tool fetch url="https://api.nic.ru/dyndns/checkip/" as-value output=user];
			:if (($tempAr->"status") = "finished") do={
				:set ($tempAr->"data") [:pick ($tempAr->"data") ([:find ($tempAr->"data") ": " -1] + 2) [:find ($tempAr->"data") "</body>" -1]];
				:set c [:find ($tempAr->"data") "/"];
				:if ([:typeof $c] = "num") do={ :set ($tempAr->"data") [:pick ($tempAr->"data") 0 $c]; }
				:if ([$DDNSCheckIPTypeFunc ($tempAr->"data") $LP] = "EXT") do={
					:set DefExternalIP [:toip ($tempAr->"data")];
				} else {
					:set t "Failed! ";
				}
			} else {
				:set t "Failed! ";
			}
		} on-error {
			:set t "Failed! ";
		}
		:set Counter ($Counter + 1);
	}
	:if ($DefExternalIP != "") do={
		:log debug ($LP . "External IP of active default WAN found (" . ($tempAr->"data") . ")");
	} else {
		:log debug ($LP . "External IP of active default WAN not found with 5 attempts (possibly connection or DNS problems).");
	}
	# CHECKING IF EXTERNAL IP FOUND AND EXISTS IN WHITE LIST (IF DUAL NAT CHECK USED)
	:if ($DefExternalIP != "") do={
		:set c [:len $WhiteListAr];
		:if ($c > 0) do={
			:set Found false;
			:set Counter 0;
			:while (($Found = false) && ($Counter < $c)) do={
				:if (($DefExternalIP = ($WhiteListAr->$Counter)) || ($DefExternalIP in ($WhiteListAr->$Counter))) do={
					:set Found true;
					:log debug ($LP . "External IP found in white-list (dual NAT check passed).");
					}
				:set Counter ($Counter + 1);
			}
			:if ($Found = false) do={
				:set DefExternalIP "";
				:log warning ($LP . "External IP not found in white-list (probable dual NAT exists).");
			}
		}
	}
} else {
	:log debug ($LP . "External default WAN IP check not needed.");
}

#MAKE A LIST OF DOMAINS TO CHECK THEIR IP ADDRESSES
:local DomainsToResAr [:toarray ""];
:if (([:tostr $DefExternalIP] != "") && ([:len $DomainsDefAr] > 0)) do={
	:foreach val in=$DomainsDefAr do={
		:set ($DomainsToResAr->$val) [:tostr $DefExternalIP];
	}
}
:if ([:len $DomainsWANsAr] > 0) do={
	:foreach key,val in=$DomainsWANsAr do={
		:if ($val != "") do={
			:set t [:typeof [:find $WANsActiveAr $key]];
			:foreach v in=[:toarray $val] do={
				:if ($t = "num") do={
					:set ($DomainsToResAr->$v) ($WANsIPsAr->$key);
				} else {
					:if (($DefExternalIP != "") && ([:typeof [:find $DomainsToDefAr $v]] = "num")) do={
						:set ($DomainsToResAr->$v) [:tostr $DefExternalIP];
					}
				}
			}
		}
	}
}

# COLLECTING DNS SERVERS
:local DNSServersAr [:toarray ""];
:set tAr [:toarray ""];
:set temp "";
:do { :set temp [:tostr [/ip dns get servers]]; } on-error {}
:if ($temp != "") do={ :set tAr ($tAr, [:toarray [$DDNSReplCharsFunc $temp ";" ","]]); }
:do { :set temp [:tostr [/ip dns get dynamic-servers]]; } on-error {}
:if ($temp != "") do={ :set tAr ($tAr, [:toarray [$DDNSReplCharsFunc $temp ";" ","]]); }
:set tAr ($tAr, {"8.8.8.8";"8.8.4.4"});
:foreach val in=$tAr do={
	:if ([:typeof [:find $DNSServersAr $val]] != "num") do={ :set DNSServersAr ($DNSServersAr , $val); }
}

# RESOLVING AND UPDATING (IF NEEDED) IPs FOR EVERY DOMAIN
:set temp "";
:set c [:len $DNSServersAr];
:set Counter 0;
:foreach key,val in=$DomainsToResAr do={
:local ResolvedIP false;
	:if ($temp != "") do={ :set c 1; }
	:set Count 0;
	:while (($ResolvedIP = false) && ($Count < $c)) do={
		:if ($temp != "") do={ :set t $temp; } else { :set t ($DNSServersAr->$Count); }
		:do {
			:log debug ($LP . "Trying to resolve \"" . $key . "\" via \"" . $t . "\" DNS server...");
			:set tmp [:tostr [:resolve $key server=[:toip $t]]];
			:if ([$DDNSCheckIPTypeFunc $tmp $LP] = "EXT") do={
				:set temp $t;
				:set ResolvedIP $tmp;
				:log debug ($LP . "Resolve success.");
			} else {
				:log warning ($LP . "Resolve failed (not external type of IP).");
			}
		} on-error {
			:log warning ($LP . "Resolve failed (probably the host not exists, or DNS server not working).");
			:set ResolvedIP false;
		}
		:set Count ($Count + 1);
	}
	:if ($ResolvedIP != false) do={
		:if ($ResolvedIP != $val) do={
			:log info ($LP . "Resolved IP (" . $ResolvedIP . ") for \"" . $key . "\" differs from appointed WAN IP (" . $val . "), updating...");
			:set t "";
			:set Found false;
			:set Count 0;
			:while (($Found = false) && ($Count < 5)) do={
				:if ($Count > 0) do={ :delay 5; }
				:do {
					:log debug ($LP . $t . "Try number " . ($Count + 1) . " of 5...");
					:set tempAr [/tool fetch user=$StUser password=$StPass url=("https://api.nic.ru/dyndns/update?hostname=" . $key . "&myip=" . $val) as-value output=user];
					:if (($tempAr->"status") = "finished") do={
						:if ([:typeof [:find ($tempAr->"data") ("good " . $val)]] = "num") do={
							:set Found true;
							:delay 1;
						} else {
							:set t "Failed! ";
						}
					} else {
						:set t "Failed! ";
					}
				} on-error {
					:set t "Failed! ";
					
				}
				:set Count ($Count + 1);
			}
			:if ($Found = true) do={
				:log info ($LP . "Successfully updated IP for \"" . $key . "\".");
			} else {
				:log warning ($LP . "Failed to update IP for \"" . $key . "\" with 5 attempts (possibly connection or authentication problems).");
			}
		} else {
			:log debug ($LP . "Resolved IP for \"" . $key . "\" same as appointed WAN IP (" . $val . "), no acton taken.");
		}
	}
}

# CLEARING DNS CACHE
if ($Counter > 0) do={
	:do { /ip dns cache flush; } on-error {}
	:log debug ($LP . "A total of " . $Counter  . " DNS records have been successfully updated, cache cleared.");
}


Ответить