From e0f9f9bcf25fd11fad490fcfe3586628baf22edd Mon Sep 17 00:00:00 2001 From: vados-dev Date: Fri, 26 Jun 2026 17:01:55 +0300 Subject: [PATCH] Backup for MikroGig created successfully at Jun 26 17:01:55. --- .../20260626/20260626170150-Binary.backup | Bin 0 -> 166925 bytes MikroGig/20260626/20260626170150-Config.rsc | 2705 ++++++++++++ .../20260626/20260626170150-Diff-Config.rsc | 15 + .../20260626/20260626162002-Config.rsc | 3691 +++++++++++++++++ .../20260626/20260626164002-Config.rsc | 3690 ++++++++++++++++ 5 files changed, 10101 insertions(+) create mode 100644 MikroGig/20260626/20260626170150-Binary.backup create mode 100644 MikroGig/20260626/20260626170150-Config.rsc create mode 100644 MikroGig/history/20260626/20260626170150-Diff-Config.rsc create mode 100644 MikroMaster/20260626/20260626162002-Config.rsc create mode 100644 MikroMaster/20260626/20260626164002-Config.rsc diff --git a/MikroGig/20260626/20260626170150-Binary.backup b/MikroGig/20260626/20260626170150-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..970b90af16c4b9b846fae2e4286ac769c768516a GIT binary patch literal 166925 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%0D^;^pjwZ@{BBH!z__*$&8+>I*oDDM!cut5F^~S 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 N7y`dB{iIG#BM#J_1l0fl literal 0 HcmV?d00001 diff --git a/MikroGig/20260626/20260626170150-Config.rsc b/MikroGig/20260626/20260626170150-Config.rsc new file mode 100644 index 0000000..18dd196 --- /dev/null +++ b/MikroGig/20260626/20260626170150-Config.rsc @@ -0,0 +1,2705 @@ +# 2026-06-26 16:01:51 by RouterOS 7.23.1 +# software id = 2961-C4ZZ +# +# model = RBD52G-5HacD2HnD +# serial number = HCX084RAVFA +/interface bridge +add admin-mac=18:FD:74:6E:58:14 auto-mac=no name=Bridge-LAN port-cost-mode=\ + short +/interface wireless +# managed by CAPsMAN +# channel: 2422/20-Ce/gn(17dBm), SSID: vados, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + name=wlan1 ssid=vados wps-mode=disabled +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(20dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + name=wlan2 ssid=vados5g wps-mode=disabled +/interface veth +add address=172.18.20.2/30 container-mac-address=00:00:00:00:00:00 dhcp=no \ + gateway=172.18.20.1 gateway6="" name=docker-awg-veth +/interface list +add name=WAN +add name=LAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroTik +/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 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 comment="Download custom Certificates By Routers Identity " \ + dont-require-permissions=no name=mod/AM-Certs-By-Ident owner=\ + mod/AM-Certs-By-Ident policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Certs-By-Ident\r\ + \n# Script comment: Download custom Certificates By Routers Identity \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# CloudFlare\r\ + \n#/tool/fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobalR\ + ootCA.crt.pem;\r\ + \n#/certificate/import file-name=DigiCertGlobalRootCA.crt.pem passphrase=\ + \"\";\r\ + \n#/tool/ fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobal\ + G2TLSRSASHA2562020CA1-1.crt.pem;\r\ + \n#/certificate/import file-name=DigiCertGlobalG2TLSRSASHA2562020CA1-1.crt\ + .pem passphrase=\"\";\r\ + \n# Quad9\r\ + \n#/tool/fetch mode=https url=\"https://cacerts.digicert.com/DigiCertGloba\ + lG3TLSECCSHA3842020CA1-1.crt.pem\"\r\ + \n#/certificate/import file-name=DigiCertGlobalG3TLSECCSHA3842020CA1-1.crt\ + .pem passphrase=\"\";\r\ + \n:global tmpAddCerts true; \r\ + \n\r\ + \n:global GlobalScriptsUrl;\r\ + \n:local UrlSuffix [/system/identity/get name];\r\ + \n:local UrlDir \"certs/\";\r\ + \n:local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\");\r\ + \n\r\ + \n:log error \$CertUrl;\r\ + \n\r\ + \n:local CertName \"DigiCert-Global-Root-CA.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-CA\" [ find where common-na\ + me=\"DigiCert Global Root CA\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-CA.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-CA\" [ find where commo\ + n-name=\"DigiCert Assured ID Root CA\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-G2\" [ find where commo\ + n-name=\"DigiCert Assured ID Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Assured-ID-Root-G3.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Assured-ID-Root-G3\" [ find where commo\ + n-name=\"DigiCert Assured ID Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-G2-TLS-RSA-SHA256-2020-CA1\" [ f\ + ind where common-name=\"DigiCert Global G2 TLS RSA SHA256 2020 CA1\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-G3-TLS-ECC-SHA384-2020-CA1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-G3-TLS-ECC-SHA384-2020-CA1\" [ f\ + ind where common-name=\"DigiCert Global G3 TLS ECC SHA384 2020 CA1\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-Root-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-G2\" [ find where common-na\ + me=\"DigiCert Global Root G2\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Global-Root-G3.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Global-Root-G3\" [ find where common-na\ + me=\"DigiCert Global Root G3\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-TLS-ECC-P384-Root-G5.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-TLS-ECC-P384-Root-G5\" [ find where com\ + mon-name=\"DigiCert TLS ECC P384 Root G5\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-TLS-RSA4096-Root-G5.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-TLS-RSA4096-Root-G5\" [ find where comm\ + on-name=\"DigiCert TLS RSA4096 Root G5\" ];\r\ + \n\r\ + \n:set CertName \"DigiCert-Trusted-Root-G4.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"DigiCert-Trusted-Root-G4\" [ find where common-n\ + ame=\"DigiCert Trusted Root G4\" ];\r\ + \n\r\ + \n:set CertName \"GTS-Root-R4.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"GTS-Root-R4\" [ find where common-name=\"GTS Roo\ + t R4\" ];\r\ + \n\r\ + \n:set CertName \"Go-Daddy-Root-Certificate-Authority-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Go-Daddy-Root-Certificate-Authority-G2\" [ find \ + where common-name=\"Go Daddy Root Certificate Authority - G2\" ];\r\ + \n\r\ + \n:set CertName \"ISRG-Root-X1.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"ISRG-Root-X1\" [ find where common-name=\"ISRG R\ + oot X1\" ];\r\ + \n\r\ + \n:set CertName \"Root-YE.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Root-YE\" [ find where common-name=\"Root YE\" ]\ + ;\r\ + \n\r\ + \n:set CertName \"Starfield-Root-Certificate-Authority-G2.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"Starfield-Root-Certificate-Authority-G2\" [ find\ + \_where common-name=\"Starfield Root Certificate Authority - G2\" ];\r\ + \n\r\ + \n:set CertName \"USERTrust-RSA-Certification-Authority.pem\";\r\ + \n/tool/fetch mode=https url=(\$CertUrl . \$CertName);\r\ + \n/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n/certificate/set name=\"USERTrust-RSA-Certification-Authority\" [ find w\ + here common-name=\"USERTrust RSA Certification Authority\" ];\r\ + \n\r\ + \n:global tmpAddCerts (a);\r\ + \n\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#:set CertName \"GTS-Root-RX.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#internet\r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#:set CertName \"ISRG-Root-X2.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n\r\ + \n\r\ + \n#/ip/dns/set allow-remote-requests=yes doh-max-concurrent-queries=100 do\ + h-max-server-connections=20 use-doh-server=https://security.cloudflare-dns\ + .com/dns-query verify-doh-cert=yes;\r\ + \n#/ip/dns/static remove [find where address=1.1.1.1];\r\ + \n#/ip/dns/static add address=1.1.1.1 name=security.cloudflare-dns.com com\ + ment=\"cloudflare-dns IPv4 1\";\r\ + \n#/ip/dns/ static remove [find where address=1.0.0.1];\r\ + \n#/ip/dns/static add address=1.0.0.1 name=security.cloudflare-dns.com com\ + ment=\"cloudflare-dns IPv4 2\"\r\ + \n#/ip dns static add address=2606:4700:4700::1111 name=security.cloudflar\ + e-dns.com type=AAAA\r\ + \n#/ip dns static add address=2606:4700:4700::1001 name=security.cloudflar\ + e-dns.com type=AAAA\r\ + \n#/ip/dns set servers=\"1.0.0.1, 8.8.4.4\";\r\ + \n#/ip/dns set servers=\"\";" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n:global GlobalScriptsUrl;\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \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" +/certificate settings +set builtin-trust-store=fetch crl-store=system +/container +# download/extract error: check registry failed: unsupported registry api version: +add hostname=amnezia interface=docker-awg-veth layer-dir="" logging=yes \ + mountlists=awg_conf name=awg-mikrotik-arm:latest remote-image=\ + awg-mikrotik-arm:latest root-dir=/usb1/docker/awg start-on-boot=yes +/container config +set memory-high=2048.0KiB registry-url=https://reg.vados.ru tmpdir=/tmp \ + username=amster-dev +/container mounts +add dst=/etc/amnezia/amneziawg list=awg_conf src=/wg +/disk settings +set auto-media-interface=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether1 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=ether4 +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/ip neighbor discovery-settings +set discover-interface-list=Internet +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ + forward=no +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=Bridge-LAN list=WAN +add interface=ether2 list=LAN +add interface=ether3 list=LAN +add interface=ether4 list=LAN +add interface=ether5 list=LAN +add interface=wlan2 list=LAN +add interface=wlan1 list=LAN +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-18FD746E5814 discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan1,wlan2 lock-to-caps-man=yes +/ip address +add address=10.30.30.86/24 interface=Bridge-LAN network=10.30.30.0 +add address=172.18.20.1/30 interface=docker-awg-veth network=172.18.20.0 +/ip dns +set servers=10.30.30.81 +/ip firewall mangle +# 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-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 irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip hotspot walled-garden +add server=*1 +add server=*1 +/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=ping 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=To-VPN +/ip service +set ftp disabled=yes +set telnet 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 port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set graphs-plain=no rest-plain=no webfig-plain=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=109.107.177.79 interface=*A type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 nd +set [ find default=yes ] advertise-dns=yes +/system clock +set time-zone-name=Europe/Amsterdam +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroGig +/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=10.30.30.81 +add address=10.30.30.10 +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 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 +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com diff --git a/MikroGig/history/20260626/20260626170150-Diff-Config.rsc b/MikroGig/history/20260626/20260626170150-Diff-Config.rsc new file mode 100644 index 0000000..6dd4b97 --- /dev/null +++ b/MikroGig/history/20260626/20260626170150-Diff-Config.rsc @@ -0,0 +1,15 @@ +2572,2575c2572,2574 +< # download/extract error: check registry failed: unsupported registry api version: +< add hostname=amnezia interface=docker-awg-veth layer-dir="" logging=yes \ +< mountlists=awg_conf name=awg-mikrotik-arm:latest remote-image=\ +< awg-mikrotik-arm:latest root-dir=/usb1/docker/awg start-on-boot=yes +--- +> add file=usb1/tmp/awg-mikrotik-arm.tar hostname=amnezia interface=\ +> docker-awg-veth layer-dir="" logging=yes mountlists=awg_conf name=\ +> awg-mikrotik-arm root-dir=/usb1/docker/awg start-on-boot=yes +2577,2578c2576,2577 +< set memory-high=2048.0KiB registry-url=https://reg.vados.ru tmpdir=/tmp \ +< username=amster-dev +--- +> set memory-high=2048.0KiB registry-url=reg.vados.ru tmpdir=/tmp username=\ +> amster-dev diff --git a/MikroMaster/20260626/20260626162002-Config.rsc b/MikroMaster/20260626/20260626162002-Config.rsc new file mode 100644 index 0000000..6a07b0b --- /dev/null +++ b/MikroMaster/20260626/20260626162002-Config.rsc @@ -0,0 +1,3691 @@ +# 2026-06-26 16: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/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 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" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes 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 +#error exporting "/ip/ipsec/key/psk" (timeout) +/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/20260626/20260626164002-Config.rsc b/MikroMaster/20260626/20260626164002-Config.rsc new file mode 100644 index 0000000..de71c0c --- /dev/null +++ b/MikroMaster/20260626/20260626164002-Config.rsc @@ -0,0 +1,3690 @@ +# 2026-06-26 16: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 +/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/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 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" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes 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 -- 2.52.0