From c94c8940b293385c0562d294a3f6c8f8a27f080f Mon Sep 17 00:00:00 2001 From: vados-dev Date: Tue, 30 Jun 2026 12:42:27 +0300 Subject: [PATCH] Backup for MikroGig created successfully at Jun 30 12:42:27. --- .../20260630/20260630124223-Binary.backup | Bin 0 -> 177026 bytes MikroGig/20260630/20260630124223-Config.rsc | 2776 +++++++++++++ .../20260630/20260630124223-Diff-Config.rsc | 8 + .../20260630/20260630052003-Config.rsc | 3692 +++++++++++++++++ .../20260630/20260630100002-Config.rsc | 3691 ++++++++++++++++ .../20260630/20260630102001-Config.rsc | 3692 +++++++++++++++++ MikroMaster/tmp/Diff-Config.rsc | 4 - 7 files changed, 13859 insertions(+), 4 deletions(-) create mode 100644 MikroGig/20260630/20260630124223-Binary.backup create mode 100644 MikroGig/20260630/20260630124223-Config.rsc create mode 100644 MikroGig/history/20260630/20260630124223-Diff-Config.rsc create mode 100644 MikroMaster/20260630/20260630052003-Config.rsc create mode 100644 MikroMaster/20260630/20260630100002-Config.rsc create mode 100644 MikroMaster/20260630/20260630102001-Config.rsc delete mode 100644 MikroMaster/tmp/Diff-Config.rsc diff --git a/MikroGig/20260630/20260630124223-Binary.backup b/MikroGig/20260630/20260630124223-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..a73672ed222663a0b385d38abe96e7ce2a5fc6f8 GIT binary patch literal 177026 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 z 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%0Aa@{8=XN<^>D>j6-af=o?&KjA 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

M814n*|n=?ILvX(iT6T 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\"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/728/bebacfc7\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 11:00:05 30.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" +add dont-require-permissions=no name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + :put \"Download image, and save him to usb1/tmp\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-arm.tar\" dst-\ + path=usb1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 \\\ + \n action=dst-nat to-addresses=10.9.9.2 to-ports=55055 in-interface=eth\ + er1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 \\\ + \n action=dst-nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether\ + 1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=usb1/tmp/awg2-webui.tar interface=veth-awg hostname=\ + awg2-webui \\\ + \nroot-dir=usb1/awg2-webui envlist=awgui-env start-on-boot=yes logging=yes\ + \_shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \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" +/container +# killed due to out of memory +add cpu-list=cpu1,cpu3 domain-name=vados.ru envlists=awgui-env file=\ + usb1/tmp/awg2-webui.tar hostname=mikronuc interface=veth-awg layer-dir=\ + /usb1/docker logging=yes memory-high=12.0MiB memory-max=16.0MiB name=\ + awg2-webui root-dir=/usb1/awg-webui shm-size=8.0MiB start-on-boot=yes \ + tmpfs=/tmpfs:512.0MiB:01000 +/container config +set layer-dir=/usb1/docker memory-high=2048.0KiB memory-max=2048.0KiB \ + registry-url=https://registry-1.docker.io tmpdir=/tmp username=amsternl +/container envs +add key=AWG_ENDPOINT list=awgui-env value=10.30.30.10:443 +add key=SECRET_KEY list=awgui-env value=RanDomSecret32SymboLs2325272931q +add key=WEB_PASS list=awgui-env value=zoomzoomrulezz +add key=WEB_USER list=awgui-env value=vados +/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 +add bridge=*D interface=*C +add bridge=*F interface=*E +add bridge=*11 interface=*10 +add bridge=*13 interface=*12 +add bridge=*15 interface=*14 +add bridge=br-awg interface=veth-awg +/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=static internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +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 +add interface=ether1 list=Internet +add interface=Bridge-LAN 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=ether1 network=10.30.30.0 +add address=10.9.9.1/24 interface=br-awg network=10.9.9.0 +/ip dns +set allow-remote-requests=yes servers=10.30.30.33,109.107.177.79 +/ip firewall nat +add action=masquerade chain=srcnat src-address=10.9.9.0/24 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=443 in-interface=ether1 protocol=udp \ + to-addresses=10.9.9.2 to-ports=55055 +# in/out-interface matcher not possible when interface (ether1) is slave - use master instead (Bridge-LAN) +add action=dst-nat chain=dstnat dst-port=9443 in-interface=ether1 protocol=\ + tcp to-addresses=10.9.9.2 to-ports=443 +/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=2 dst-address=0.0.0.0/0 gateway=\ + ether1 routing-table=main scope=30 target-scope=10 +/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/20260630/20260630124223-Diff-Config.rsc b/MikroGig/history/20260630/20260630124223-Diff-Config.rsc new file mode 100644 index 0000000..531db03 --- /dev/null +++ b/MikroGig/history/20260630/20260630124223-Diff-Config.rsc @@ -0,0 +1,8 @@ +485c485 +< \n:global CommitId \"main/728/bebacfc7\";\r\ +--- +> \n:global CommitId \"main/723/6e812143\";\r\ +488c488 +< r: root, in 11:00:05 30.06.2026.\";\r\ +--- +> r: root, in 10:45:05 29.06.2026.\";\r\ diff --git a/MikroMaster/20260630/20260630052003-Config.rsc b/MikroMaster/20260630/20260630052003-Config.rsc new file mode 100644 index 0000000..4c6094c --- /dev/null +++ b/MikroMaster/20260630/20260630052003-Config.rsc @@ -0,0 +1,3692 @@ +# 2026-06-30 05:20:07 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: 2422/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: 5200/20-eCee/ac/P(21dBm), 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 +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/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.0.0.1,1.1.1.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/727/203f86a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:05 29.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 +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 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=WAN 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=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add disabled=yes interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/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 +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/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.33,109.107.177.79 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=109.107.177.79,10.30.30.33,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 disabled=yes 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 disabled=yes 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 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes 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 disabled=yes 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 +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +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 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 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 in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + disabled=yes new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main 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 src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main 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="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main 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 routing-mark=!main src-address=10.30.30.0/24 +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 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 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +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 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 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" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN 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 routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +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="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=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 disabled=yes 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-plain=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 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 ip-version=ipv4 +/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/20260630/20260630100002-Config.rsc b/MikroMaster/20260630/20260630100002-Config.rsc new file mode 100644 index 0000000..bf12cec --- /dev/null +++ b/MikroMaster/20260630/20260630100002-Config.rsc @@ -0,0 +1,3691 @@ +# 2026-06-30 10:00:07 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: 2422/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 +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/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.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +#error exporting "/ip/ipsec/proposal" (timeout) +/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/727/203f86a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:05 29.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 +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 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=WAN 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=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add disabled=yes interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/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 +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/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.33,109.107.177.79 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=109.107.177.79,10.30.30.33,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 disabled=yes 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 disabled=yes 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 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes 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 disabled=yes 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 +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +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 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 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 in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + disabled=yes new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main 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 src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main 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="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main 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 routing-mark=!main src-address=10.30.30.0/24 +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 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 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +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 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 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" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN 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 routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +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="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=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 disabled=yes 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-plain=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 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 ip-version=ipv4 +/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/20260630/20260630102001-Config.rsc b/MikroMaster/20260630/20260630102001-Config.rsc new file mode 100644 index 0000000..63257b0 --- /dev/null +++ b/MikroMaster/20260630/20260630102001-Config.rsc @@ -0,0 +1,3692 @@ +# 2026-06-30 10:20:06 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2422/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 +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/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.0.0.1,1.1.1.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/727/203f86a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:05 29.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 +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 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=WAN 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=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add disabled=yes interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/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 +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/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.33,109.107.177.79 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=109.107.177.79,10.30.30.33,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 disabled=yes 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 disabled=yes 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 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes 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 disabled=yes 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 +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +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 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 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 in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + disabled=yes new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main 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 src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main 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="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main 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 routing-mark=!main src-address=10.30.30.0/24 +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 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 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +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 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 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" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN 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 routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +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="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=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 disabled=yes 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-plain=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 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 ip-version=ipv4 +/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/tmp/Diff-Config.rsc b/MikroMaster/tmp/Diff-Config.rsc deleted file mode 100644 index b0793f5..0000000 --- a/MikroMaster/tmp/Diff-Config.rsc +++ /dev/null @@ -1,4 +0,0 @@ -3509d3508 -< #error exporting "/ipv6/route" (timeout) -3614d3612 -< #error exporting "/snmp/community" (timeout) -- 2.52.0