From ef2d38c72323ac29f594ffb42b87293e1e094016 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Mon, 22 Jun 2026 21:00:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 22 21:00:03. --- .../20260622/20260622032002-Config.rsc | 3679 +++++++++++++ .../20260622/20260622052001-Config.rsc | 3689 +++++++++++++ .../20260622/20260622174002-Config.rsc | 3690 +++++++++++++ .../20260622/20260622180002-Config.rsc | 3689 +++++++++++++ .../20260622/20260622210001-Binary.backup | Bin 0 -> 363887 bytes MikroNUC/20260622/20260622210001-Config.rsc | 4880 +++++++++++++++++ .../20260622/20260622210001-Diff-Config.rsc | 8 + 7 files changed, 19635 insertions(+) create mode 100644 MikroMaster/20260622/20260622032002-Config.rsc create mode 100644 MikroMaster/20260622/20260622052001-Config.rsc create mode 100644 MikroMaster/20260622/20260622174002-Config.rsc create mode 100644 MikroMaster/20260622/20260622180002-Config.rsc create mode 100644 MikroNUC/20260622/20260622210001-Binary.backup create mode 100644 MikroNUC/20260622/20260622210001-Config.rsc create mode 100644 MikroNUC/history/20260622/20260622210001-Diff-Config.rsc diff --git a/MikroMaster/20260622/20260622032002-Config.rsc b/MikroMaster/20260622/20260622032002-Config.rsc new file mode 100644 index 0000000..9d6b30b --- /dev/null +++ b/MikroMaster/20260622/20260622032002-Config.rsc @@ -0,0 +1,3679 @@ +# 2026-06-22 03:20:05 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2442/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5260/20-Ceee/ac/DP(18dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/698/09fde8db\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 18:15:04 21.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set use-ip-firewall=yes +/ip firewall connection tracking +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set udp-timeout=10s +/ip neighbor discovery-settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Bridge-LAN list=LAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ + 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=1.0.0.1,8.8.4.4 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 match-subdomain=yes name=mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 name=MikroYellow type=A +add address=10.30.30.85 name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=wg.vados.ru list=VPN-Server +/ip firewall filter +add action=accept chain=input dst-port=18880 in-interface=Bridge-LAN \ + protocol=tcp src-address=10.30.30.0/24 +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 in-interface=Bridge-LAN protocol=tcp src-address=\ + 10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address=\ + 10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" dst-address-list=\ + VPN-Server out-interface="ether1 - Unet" protocol=udp src-port=13231 +add action=accept chain=output dst-address-list=VPN-Server dst-port=29185 \ + out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input protocol=icmp src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related in-interface-list=\ + *2000014 +add action=drop chain=input in-interface=!Bridge-LAN +/ip firewall mangle +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 \ + src-address-list=NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" dst-address=\ + !10.30.30.0/24 out-interface="ether1 - Unet" src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp src-address=\ + !10.30.30.0/24 to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing bfd configuration +add addresses=10.8.0.0/24 disabled=no forbid-bfd=yes interfaces=*E min-rx=\ + 200ms min-tx=200ms multiplier=5 vrf=main +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260622/20260622052001-Config.rsc b/MikroMaster/20260622/20260622052001-Config.rsc new file mode 100644 index 0000000..aef8460 --- /dev/null +++ b/MikroMaster/20260622/20260622052001-Config.rsc @@ -0,0 +1,3689 @@ +# 2026-06-22 05:20:04 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2442/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5260/20-Ceee/ac/DP(18dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/698/09fde8db\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 18:15:04 21.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set use-ip-firewall=yes +/ip firewall connection tracking +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set udp-timeout=10s +/ip neighbor discovery-settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Bridge-LAN list=LAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ + 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=1.0.0.1,8.8.4.4 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 match-subdomain=yes name=mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 name=MikroYellow type=A +add address=10.30.30.85 name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +/ip firewall filter +add action=accept chain=input dst-port=18880 in-interface=Bridge-LAN \ + protocol=tcp src-address=10.30.30.0/24 +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 in-interface=Bridge-LAN protocol=tcp src-address=\ + 10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address=\ + 10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" dst-address-list=\ + VPN-Server out-interface="ether1 - Unet" protocol=udp src-port=13231 +add action=accept chain=output dst-address-list=VPN-Server dst-port=29185 \ + out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input protocol=icmp src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related in-interface-list=\ + *2000014 +add action=drop chain=input in-interface=!Bridge-LAN +/ip firewall mangle +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 \ + src-address-list=NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=udp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" dst-address=\ + !10.30.30.0/24 out-interface="ether1 - Unet" src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp src-address=\ + !10.30.30.0/24 to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing bfd configuration +add addresses=10.8.0.0/24 disabled=no forbid-bfd=yes interfaces=*E min-rx=\ + 200ms min-tx=200ms multiplier=5 vrf=main +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260622/20260622174002-Config.rsc b/MikroMaster/20260622/20260622174002-Config.rsc new file mode 100644 index 0000000..15a06ab --- /dev/null +++ b/MikroMaster/20260622/20260622174002-Config.rsc @@ -0,0 +1,3690 @@ +# 2026-06-22 17:40:05 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2442/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5260/20-Ceee/ac/DP(18dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +#error exporting "/ip/ipsec/policy/group" (timeout) +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/698/09fde8db\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 18:15:04 21.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set use-ip-firewall=yes +/ip firewall connection tracking +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set udp-timeout=10s +/ip neighbor discovery-settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Bridge-LAN list=LAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ + 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=1.0.0.1,8.8.4.4 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 match-subdomain=yes name=mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 name=MikroYellow type=A +add address=10.30.30.85 name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +/ip firewall filter +add action=accept chain=input dst-port=18880 in-interface=Bridge-LAN \ + protocol=tcp src-address=10.30.30.0/24 +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 in-interface=Bridge-LAN protocol=tcp src-address=\ + 10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address=\ + 10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" dst-address-list=\ + VPN-Server out-interface="ether1 - Unet" protocol=udp src-port=13231 +add action=accept chain=output dst-address-list=VPN-Server dst-port=29185 \ + out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input protocol=icmp src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related in-interface-list=\ + *2000014 +add action=drop chain=input in-interface=!Bridge-LAN +/ip firewall mangle +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 \ + src-address-list=NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=udp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" dst-address=\ + !10.30.30.0/24 out-interface="ether1 - Unet" src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp src-address=\ + !10.30.30.0/24 to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing bfd configuration +add addresses=10.8.0.0/24 disabled=no forbid-bfd=yes interfaces=*E min-rx=\ + 200ms min-tx=200ms multiplier=5 vrf=main +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260622/20260622180002-Config.rsc b/MikroMaster/20260622/20260622180002-Config.rsc new file mode 100644 index 0000000..09d14f0 --- /dev/null +++ b/MikroMaster/20260622/20260622180002-Config.rsc @@ -0,0 +1,3689 @@ +# 2026-06-22 18:00:06 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2442/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5260/20-Ceee/ac/DP(18dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/698/09fde8db\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 18:15:04 21.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set use-ip-firewall=yes +/ip firewall connection tracking +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set udp-timeout=10s +/ip neighbor discovery-settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Bridge-LAN list=LAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ + 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=1.0.0.1,8.8.4.4 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 match-subdomain=yes name=mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 name=MikroYellow type=A +add address=10.30.30.85 name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +/ip firewall filter +add action=accept chain=input dst-port=18880 in-interface=Bridge-LAN \ + protocol=tcp src-address=10.30.30.0/24 +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 in-interface=Bridge-LAN protocol=tcp src-address=\ + 10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address=\ + 10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" dst-address-list=\ + VPN-Server out-interface="ether1 - Unet" protocol=udp src-port=13231 +add action=accept chain=output dst-address-list=VPN-Server dst-port=29185 \ + out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input protocol=icmp src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related in-interface-list=\ + *2000014 +add action=drop chain=input in-interface=!Bridge-LAN +/ip firewall mangle +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 \ + src-address-list=NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=udp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" dst-address=\ + !10.30.30.0/24 out-interface="ether1 - Unet" src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp src-address=\ + !10.30.30.0/24 to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing bfd configuration +add addresses=10.8.0.0/24 disabled=no forbid-bfd=yes interfaces=*E min-rx=\ + 200ms min-tx=200ms multiplier=5 vrf=main +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroNUC/20260622/20260622210001-Binary.backup b/MikroNUC/20260622/20260622210001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..c1dd457065e6a5e13f134dce8602db746ae70d6e GIT binary patch literal 363887 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

~;fH-&D6}m^)CQTm5Bu{oZ1A(l+CXaFY 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

`B6^x>ekod{%iCwQT`Rx&{%5RK-m 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

N);|-8x_ZPBUG~* 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 Z2B?;zTcn6`MYWk5#Gfk?V?;p(ry@CW7pu1mf z?tBN!M*0)&08WBT;C5zi`!Y1C$|NK+14z|9?qqhKUgp%F-^mXW=nIB9S^`D(>v(iq zaIo0LDh{&!63y8!OLs?QX`z^#N$&lM!?P|$Wf8=;0L00FikPom)yjm{0q77w!rotgnGTd3oPe;_7) zp$Zrj2iZc*Q-5nMW-3C$XKHwfORT

*NDj`Uj6>=!aowsod6>r@SS;(Q0+n1e6`O=Ud&>s|puSM&Eo_BL z&eZ$7>6ShLri*5s%QQ&%XUeUNJFhr0dn!pf(*0@eLnZ=h{VGxLeYl02I{y^0=oaw?G7@-8`akr1ldviw`2UewW?n3JsW`{2{y?Ibtea-B4=lgE-;2xaUIlYC&lJFgKd?&kH-9^QGW za>Z*T;j9W?&KsL}5kS4SlhTaJbMdpj*(l%e@4}PDkUuSfgp`v+SZY#Joh~aoW)!$1 zfuCJUjd~gXiTfsqbyOqEZ#<_nW6GIut+IHq`MW6_vC%eR%An74-^uQ$Y2-80hajW# zV3KfvkzEE|BMtBiZ8hGFrBX{B(a&o0=CsI<_Zd-w)$bO*zi(Hx&@w7+N(QnlnK^tDGVI*_5=0}z&dPNbz z7#W<|ap}STnRj;pYX*gqfLOb)nRY-&3G*e=PD5)0V8A|uFnLhXSpazJ!bjr}xLnI3}^2Z(leM$+Vkv5hZ+dDRR+ERxt z;Ho_qE}jbjT0eLUHd4wRfEyB{m8tjmrRrtG%>K@&fE@7lSqk{&ent{-&#WQYN|w`b zbytN4{a#@WOrLy%5`gW?qO8F&_J4wHJklG{a&{f;2h+^UH}{ zR7`dd!UY&WdKL)SInrW&1Ay7bSW-VNP_PN^RJasg zm$IQt^WzPn#HN;ly~{%CGji=kV(Fij$2kUd>(}2*! zz!jsEmG8(j<5tRRLg~c2f~h%cnIZopKPd%~9=CP@irYv3ru;Q@>8L9OpgtpJu? zpxvl*yc(!?3s$)(Ftkcf7&KlH$}gl_4I?Pfis?#6SR?;dERNWv8e9#;oNcA{Kacmy zcWZwN5&Tgj(`{_4tOrV8T9+@*0X#c}ylj%Q>1M$(^Wz0BjfP1slmwUk)@d_xG6eu3 zcr}9gHsK&coD2vmsIQu;s!*A|MaK`z$^MHf4vY-wb*s38{Id0D72F8NlQ`Th6tZkZ zzBu^J*oc2jxFe{Mx)q?;xBxqr!xpE%)9ed^J*Ryr>?o!C>Aoy7TM@NhH~w%~3JtlS z`;kThuTAZ1NLM8!gJtK^Dpj@T6OT1vqCTm6=~CMEp$x-Fp0u8MGPXC$y%d`eEEnOc zIjox{6@L>Q&Nrb6fx}kd1`W4|br5nxHz6D_lkz!P0y8xQoevcoub6%LC4$ zV*z(3xkuvy;_pS=YH~y?ebnn1SL;bsS&(uPwK$R$FRL2(owrtk$_Kz?` zv`pJ&Bl1^&dvgXY2!i3j9pP=wjUG8iO3Ylw9oky`Ts4ch8qGmO`gmUot`awO*{6p0 z{1lq0$S%Vo)5_AjAP6fuLSj{JJfwZ=^*W;AY2l%kL0%z_R`|ii4)bG0gb7r!l262D zIuwz892>t1cXjoTcnEwvFeV%qGN|LFJ`tLa2n#<(+#S zKW*knT#+)i&ag)&nig~*7u%jX$FUrZkhQ$W@~JayHVbZF0X9QQK9R@#OeMbn@NtA8 zaSKH5M`)bT7&ke1dEyLB6C(uwm4hC#$Xmp^m5RSg*c%(kuIY+n+iwK(6dabSzFUfL zKtcT~FT5^=`MH>vTIBipTAoQ%A^puEe_V|QAHuos0Dh7#!lvWg45kp*f zKpJnwy>$JjVE$O7(iG_BO#8c}W1sCc=8KKN9IsGCm$KHtxMGb&T@CRKu1jjl#oV+cqvGVQm=f|8M#wRJFln`!f|Qk zUkWdpey_8B#Pa~IuUbKeD1}^tHWLc#Z?}sv`&l4kCojOnhTW!G$q5GaI|gV3_l+=R z=c_y|%sb!;ycM*BE*uQo;wlk;EE&wgf6krf)KGKNhki35bX@P`;UE*XPaT><*m-m8 zy7t*zZJ2UwZh_GAAiP1c>F z7Cab|^Bm&${XzBpkQ|TJRqufA%B5&31;%Qij5MZW}AUGEpRRp-|u=GMYzP)791VxH(Pp!A+G?$xcOL+iP(hu#=?@_ED(mf^d z*5vk{YS8|ywi$nkTR$)doUe_^f2b!@e@G)=>VSMR`(X__XCQent*gEy%$`4{!6GsW zKg&z)fpG53y&GF!iLm#dzFm3iiA%jql_fk3w4tI~$ zM1z_yF~oQJ1H(B0Gxepfh&&8>H`QgH8*H_-IaDikTnQz&D5ZH2!yYD)X(NHf_>dL; zc^IhRNMo4W-@owQZ4+a+A4jJTS~5WgB;dw3Sf+7vk+!GOR~i3osXI`!yRh%1qSBBT zAT8%SvTL%gwxY}?QJ3@5S2lNQiUl~C1M8D;1}6XO>AzeCE=Ed(1B?i(<3R-hFq$yp zg1Wsr+f1CyHdQb3c*S=rK%l@U_rlCgmM1Mo=J#YuKi*ljbPHJ`AsFvcXp3O1sB^Y6wU2Ap1kturA>u2P2V<;{UHRmVdviw6Xp)u4xYI5dQeLEg{Qzyzw4UVY1ml=spj~Di~$a69cQ7H1`;-*To4aU30y*Gdw@Noi^AX8zg{%=`?m;; z8FEfWu5<)M+bUSp3Q^#q=s9Gx?`yLTL_yl-_WeKpLj$&kpVn^p(^|+s(1!8GC7rlK zr~gs#yKSwfpk<-}7lTb)C+g$i&`C$^6SpV+CJrO-`Hxrjx<*-b!SuW@yd2KIws-5r>3G))03PrPSU05mB4;Q3qm$p93@L)XDVFAt% z;nh%Mc@t83#p0@8HA2^5~T`<(|(=oOB?8N3vw_A!uk!uy&p=y&~a55mwc4a|( z^QddNy&;WNbL^*y#hi1!-gXIJUt|iLF`TwQB3TVnF zq2c&?Z5XC6>hPP7dk)>@S<&f-X zreC;WL13U}>{ZRa0ReSg=bf_KnT~j~a3R_mQBnLiYV7B|OiV3seKbE28=cA_X4}G= zygpK2r<9!r+EHc<98ZidDMEw{3uFYs)Ag2RdJLHkV^3moW#&ZP__;5Ox;E&^A6|1mPbYNnz^6wXnFMG5fGjqZ9vA9 zIa6hYw$pH^zH>8@ov{G-fxhn^@;2P)QhYu@O(aSXOb zx}>eTuVCgShHw(6e(BkV;xyzxCu~1~Wuy%?d@3~MhH}?$du;rL%(7J{0?ky23@CeV zdi-XPT+6=Ifrw>g2fywp8 zW@K08m(oj&#N2@XAbzyiwHe3v5OZZq(cdTzq#enuct8 zY|TqlD2Ix`VH{X{B$C50v+{s-<<26QDz#H#;}5P*NhF6KQdJbxDO`LwPUTAtWkUHa zIBjTBqVuuO!-QSVC$@vQ!DM(mK?gegi*%spz3bPW8@4#&kP7<>06* z+eE(@Y6$|WtM>Ua-=<9o{+7P(zdeS`i8ex|0iaX+yE6E%Cf?qx3MCd3aLfnj@W1os zC#PHUtqNynoFbx0Xz*(L%8C7AnZNes(9&A#kA5*Iyby?bpQ5A?W)@y!KwuS(jyF>b zU&O`B>^v%HY{0WY%k<@jbnQB^i3-SFspkNVT`wzJDqVNiB&sk=u~>1(czX`G2BeN} zQ~$Z$1dEg+W~30kWXd(J%7M7n&HB^x2-!`*$3)}@dSV~=t=4*LMA01Uf?2`k3yqVf zDjKLCGXURnDsWTln(cGUo`#yl%MvrEfzJWpQHVSf`#$`$KY*^`0N(UgQO zje9a;>1xOsByWW!_=iYkw|9uu#aexqNvK_FIV_buegwvc1)>b$t#}4_HT5HbmoqvV zR?V7%2A}c|l5QCtS>gMc2i*o_CFdD+XRJWOHs42pcMpe+W_OAhw_XC0n0;{G2dt82 z6ZG>Ry&L7Y4OO?JNY1Bx_t`Re(XxpFN#rsgA zw>Z8rMQ7ew&-bt0mL4Y9rc%*$?hTN3b^gK!jSd-mi241DkKaYiqC8S#5C6OImJ51A zHUvKmoYWHCWM}oYl>NCN+$F9m zpawl$0(&|d^1cYHf+!B9mh_$0_xrdUozj`4@6;IG0>MbFst_r@d;-cF#T-*>s^w_h zu7y!!T6FN0q#Yx4RsP;_x)@XEf-^>^x}FdgZ)fdHzk6v=KT+I)Bx*LtLF6w1h(^iU zb(~(q!(w~pY?eSHXSPMMe6~?iiLR$!BVA-uz$W^pCceH#Ymik!uQv$!gSU~ZiYXzm ze*pS2Xw(QjtlIlXy0e6w+&m06lwE=3@pW}IyZ>G-a|%_xmhn|yPYgz)b@JzE3*HqF zw#n}Ab6K5`reXA1fec=Df~dxixYi$XY8AN~KE_!`0$LRGW8!ak(_)_G`LU~AoyXOk zmMH;)HGaic-;6d+V!oC=Z|TNhkscF)s6dmE30RNARM&{w7+qEoCsl#CYL~^p_v@+9 z@*?oc=Y&{-@A;ov`=~I){H+`0Tze)55!Ailr|lAeEte_?@S^G7q9@GA`SP^PG+5y! zOzrGvk7;;f;x6=rv7i)<1U6W~hZ2tFs=VT>uxdD+zW<)`P@bC)&PrgIMCBLRq|uzX zjXrqQhY+&N3;j3<}F!OfemoNf-Q5h)!cf!l5bz(_&{1srE23 z3qZE6uhvBV)UrLPBQMqU90$~@8lyv zAI%E+nnGw2X6?sOcnGv0?41P&%d%N3y+*o?cJn}aC{sKL1%=KMDSuuVpY1RDnrcG! zD3vM1{ftqeGjdVGJ{~@v6mIToYVK-wfH6*#>TEFBof3_L!nz(wc6N*reFTjE9}JLF$J;Y>1unb9s~E z!R$c$dNMKgGr9nQUZG~kFpkE?2SAvXAeN+GkPiJIWfPFI^i$B&ZRfP5wP6?-KXi^N zJl$r?FWlPI2HNl5TlF-tk*{JG!Q3HseVI#xrNKctK9!n+ApLAJ7&$zT?$k^#f|px( zU-xEj&l!NP=7`)JI-lcQ4v*sVV;|uuu5a4-V_1eDua0LDF*j+hGcPBV-E^)oaAJJI zy+h8t1u{z<1w!$`222jyK*;COo&^LSmch~AB=l6ew^uR-aX>fpAcACLd`Xm41?i_W zD)GiOC5YZNys^7J>icqhA7v-#wlkVz`IjIc>BT8gZRi^bOx zlPL+qng&WAPg2M*GYmtqT<-w!U<5WbB^A>;9xNUxKUC#=EkwxkDTvQlW4lwil%QOIItLV-ageHYx~fgimwnx`ZLB3j zOf~$NX8myNCO{oI{jbfp#G8fvalpxNgB;(AGiDPRn#5u$Z>sbNrvX4!PnzA;K)^b= zTP<)mpxW`VL1rKXU$VK3j#XlLCLO`7JS>ov6pi$k&uL7s2%1Hi34PYeF)ChpoTpbm})q?6=_CRw@N0uq3*ypV_EYy-@tDJ?Hhy z*i1rD9iMVs`_#}}^brJ{G3*+Rk>NcF6To+zZl5GKTmC8v(RQY;7mS5K9SjU5Om(

#m~=l3cBF2NdtBvt)LuwF6Hy2JQ4k7<|mw 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

+!kvja0Pe>Z-2T^1iRU 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\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\r\ + \n#\r\ + \n#root.mnuc.backuper\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n#:local FetchFile AM-DOHCert.rsc\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n:global MkDir;\r\ + \n\r\ + \n:local FetchFile Binary.backup\r\ + \n:local localDir \"[usb1]/scripts/\";\r\ + \n\r\ + \n\$MkDir \$localDir;\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=AmneziaWG owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + /routing table \ + \n#add disabled=no fib name=r_to_vpn\ + \n#/ip firewall address-list\ + \n#add address=8.8.4.4 list=to_vpn\ + \n#/ip firewall address-list\ + \n#add address=10.0.0.0/8 list=RFC1918\ + \n#add address=172.16.0.0/12 list=RFC1918\ + \n#add address=192.168.0.0/16 list=RFC1918\ + \n#/interface/list/add name=WAN\ + \n#/interface/list/member/add list=WAN interface=ether1\ + \n\ + \n/ip firewall mangle\ + \nadd action=accept chain=prerouting dst-address-list=RFC1918 in-interface\ + -list=!WAN\ + \n/ip firewall mangle\ + \nadd action=mark-connection chain=prerouting connection-mark=no-mark dst-\ + address-list=to_vpn in-interface-list=!WAN \\\ + \n new-connection-mark=to-vpn-conn passthrough=yes\ + \n/ip firewall mangle\ + \nadd action=mark-routing chain=prerouting connection-mark=to-vpn-conn in-\ + interface-list=!WAN new-routing-mark=r_to_vpn \\\ + \n passthrough=yes\ + \n/ip firewall mangle\ + \nadd action=mark-connection chain=output connection-mark=no-mark \\\ + \n dst-address-list=to_vpn new-connection-mark=to-vpn-conn-local \\\ + \n passthrough=yes\ + \nadd action=mark-routing chain=output connection-mark=to-vpn-conn-local \ + \\\ + \n new-routing-mark=r_to_vpn passthrough=yes\ + \n\ + \n" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=sata1 lan-bridge=Bridge-LAN registry-mirrors="https:/\ + /reg.vados.ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set builtin-trust-store=fetch crl-store=system +/container config +set layer-dir=/sata1 registry-url=https://reg.vados.ru tmpdir=/ramstorage \ + username=amster-dev +/container envs +add key=ENV list=carkeeperbot_envs value=/root/.ashrc +add key=PATH list=carkeeperbot_envs value=\ + /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +add key=PORT list=carkeeperbot_envs value="\${SSH_PORT}" +add key=SSH_PORT list=carkeeperbot_envs value=8222 +/container mounts +add dst=/etc/amnezia/amneziawg list=awg_conf src=/wg +add dst=/app list=carkeeper_app src=/sata1/carkeeper/app +add dst=/configs list=carkeeper_configs src=/sata1/carkeeper/configs +add dst=/root/.ashrc list=carkeeper_configs/.profile src=\ + /sata1/carkeeper/configs/.profile +add dst=/etc/php82/php.ini list=carkeeper_configs/php.ini src=\ + /sata1/carkeeper/configs/php.ini +add dst=/etc/ssh/sshd_config list=carkeeper_configs/sshd_config src=\ + /sata1/carkeeper/configs/sshd_config +add dst=/scripts list=carkeeper_scripts src=/sata1/carkeeper/scripts +add dst=/var/sync list=carkeeper_sync src=/sata1/carkeeper/sync +add dst=/var/tmp list=carkeeper_tmp src=/sata1/carkeeper/tmp +/interface bridge port +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-DOC interface=veth1 +add bridge=Bridge-LAN interface=LAN +add bridge=Bridge-LAN interface=dynamic +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=all internet-interface-list=WAN lan-interface-list=\ + LAN wan-interface-list=WAN +/interface list member +add interface=Bridge-LAN list=WAN +add interface=lo list=LAN +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.10.10.1/24 disabled=yes interface=Bridge-DOC network=\ + 10.10.10.0 +add address=172.18.20.5/30 disabled=yes interface=docker-xray-vless-veth \ + network=172.18.20.4 +add address=172.18.20.1/30 interface=docker-awg-veth network=172.18.20.0 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=Bridge-LAN \ + lease-script=lease-script lease-time=10m name=nucDHCP server-address=\ + 10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.81 verify-doh-cert=yes +/ip firewall address-list +add address=8.8.4.4 list=to_vpn +add address=10.0.0.0/8 list=RFC1918 +add address=172.16.0.0/12 list=RFC1918 +add address=192.168.0.0/16 list=RFC1918 +/ip firewall filter +add action=accept chain=input +add action=accept chain=output +add action=accept chain=forward comment="Comment of the rule" disabled=yes +/ip firewall mangle +# docker-xray-vless-veth not ready +add action=change-mss chain=forward new-mss=1360 out-interface=\ + docker-xray-vless-veth protocol=tcp tcp-flags=syn tcp-mss=1420-65535 +add action=accept chain=prerouting dst-address-list=RFC1918 \ + in-interface-list=!WAN +add action=mark-connection chain=prerouting connection-mark=no-mark \ + dst-address-list=to_vpn in-interface-list=!WAN new-connection-mark=\ + to-vpn-conn +add action=mark-routing chain=prerouting connection-mark=to-vpn-conn \ + in-interface-list=!WAN new-routing-mark=r_to_vpn +add action=mark-connection chain=output connection-mark=no-mark \ + dst-address-list=to_vpn new-connection-mark=to-vpn-conn-local +add action=mark-routing chain=output connection-mark=to-vpn-conn-local \ + new-routing-mark=r_to_vpn +# docker-awg-veth not ready +add action=change-mss chain=forward new-mss=1360 out-interface=\ + docker-awg-veth protocol=tcp tcp-flags=syn tcp-mss=1420-65535 +/ip firewall nat +# docker-xray-vless-veth not ready +add action=masquerade chain=srcnat out-interface=docker-xray-vless-veth +# docker-awg-veth not ready +add action=masquerade chain=srcnat out-interface=docker-awg-veth +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=bfd disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +add distance=1 dst-address=0.0.0.0/0 gateway=172.18.20.2 routing-table=\ + r_to_vpn +/ip service +set ftp disabled=yes +set www disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api disabled=yes +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both password-authentication=no \ + publickey-authentication-options=verify-required strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=external +add interface=Bridge-DOC type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" interval=30s name=mod/AM-TG-Bot on-event=\ + "/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-18 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260622/20260622210001-Diff-Config.rsc b/MikroNUC/history/20260622/20260622210001-Diff-Config.rsc new file mode 100644 index 0000000..3d08d86 --- /dev/null +++ b/MikroNUC/history/20260622/20260622210001-Diff-Config.rsc @@ -0,0 +1,8 @@ +852c852 +< \n:global CommitId \"main/700/f14f095d\";\r\ +--- +> \n:global CommitId \"main/698/09fde8db\";\r\ +855c855 +< r: root, in 13:30:03 22.06.2026.\";\r\ +--- +> r: root, in 18:15:04 21.06.2026.\";\r\ -- 2.52.0