From 358c1182c78ade07a8a538a31df088fb7943566c Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 3 May 2026 16:22:38 +0300 Subject: [PATCH] Backup for MikroMaster created successfully at May 03 16:22:38. --- .../20260503/20260503162218-Binary.backup | Bin 0 -> 214202 bytes .../20260503/20260503162218-Config.rsc | 4085 ++++++++++++++ .../20260503/20260503162218-Diff-Config.rsc | 3747 +++++++++++++ MikroNUC/20260503/20260503132602-Config.rsc | 4786 +++++++++++++++++ 4 files changed, 12618 insertions(+) create mode 100644 MikroMaster/20260503/20260503162218-Binary.backup create mode 100644 MikroMaster/20260503/20260503162218-Config.rsc create mode 100644 MikroMaster/history/20260503/20260503162218-Diff-Config.rsc create mode 100644 MikroNUC/20260503/20260503132602-Config.rsc diff --git a/MikroMaster/20260503/20260503162218-Binary.backup b/MikroMaster/20260503/20260503162218-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..9426dd7628a8088948fac179b3cf0e7afc418ba3 GIT binary patch literal 214202 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

(ykxu`ONq|hS7&Us=4W|yEL?z@rpp2~wt%wke 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 z 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 literal 0 HcmV?d00001 diff --git a/MikroMaster/20260503/20260503162218-Config.rsc b/MikroMaster/20260503/20260503162218-Config.rsc new file mode 100644 index 0000000..6de05de --- /dev/null +++ b/MikroMaster/20260503/20260503162218-Config.rsc @@ -0,0 +1,4085 @@ +# 2026-05-03 16:22:19 by RouterOS 7.22.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/caps-man configuration +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX country=belarus datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes name=cfg-2ghz ssid=vados +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX country=belarus datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes name=cfg-5ghz-ac ssid=vados5g +add channel.band=5ghz-a/n .control-channel-width=20mhz .extension-channel=XX \ + country=belarus datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes name=cfg-5ghz-an ssid=vados5g +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" mac-address=\ + 48:8F:5A:8E:1A:C2 name="ether1 - Unet" +set [ find default-name=ether3 ] comment=LEN +set [ find default-name=ether4 ] comment=NUC +/interface l2tp-server +add comment=Server disabled=yes name=l2tp-in1 user=vados +/interface l2tp-client +add comment="Starless 2 MikroSPb" connect-to=176.53.203.171 name=l2tp-EoIP \ + user=vados +add allow=mschap2 allow-fast-path=yes comment=Starless connect-to=\ + vpn.starless.ru max-mru=1280 name=l2tp-vpn use-ipsec=yes use-peer-dns=yes \ + user=vados +/interface wireguard +add listen-port=13231 mtu=1280 name=WG +/disk +add slot=tmpfs tmpfs-max-size=22369621 type=tmpfs +/interface list +add name=WAN +add name=LAN +/interface lte apn +set [ find default=yes ] ip-type=ipv4 use-network-apn=no +/interface wifi channel +add band=5ghz-ax disabled=no frequency=5180,5200,5220,5240 name=5G-AX +add band=5ghz-ac disabled=no frequency=5180,5200,5220,5240 name=5G-AC +add band=2ghz-n disabled=no frequency=2422,2442,2462 name=2G-N +add band=2ghz-ax disabled=no frequency=2422,2442,2462 name=2G-AX +add band=2ghz-g disabled=no frequency=2422,2442,2462 name=2G-G +add band=5ghz-n disabled=no frequency=5180,5200,5220,5240 name=5G-AN +/interface wifi security +add authentication-types=wpa2-psk,wpa3-psk disabled=no group-encryption=ccmp \ + group-key-update=1h management-protection=allowed name=sec2g +add authentication-types=wpa3-psk disable-pmkid=yes disabled=no \ + group-encryption=ccmp group-key-update=5m management-encryption=cmac \ + management-protection=required name=sec5G +/interface wifi configuration +add channel=5G-AX disabled=no mode=ap name=5G-AX security=sec5G security.ft=\ + yes .ft-over-ds=yes ssid=vados +add channel=5G-AN disabled=no mode=ap name=5G-AN security=sec5G ssid=vados +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroTik +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +/ip dns forwarders +add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/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="#\ + /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#:local Script AM-setup\ + \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\");\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n#\ + \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#\ + \n#root.mnuc.backuper\ + \n\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n#:local Script AM-GlobalConfig;\ + \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\");\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\ + \n:global MkDir;\ + \n\ + \n:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n#\$MkDir \$localDir;\ + \n\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe;\r\ + \n:global RunOnce;\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs true;\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 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 :local CertSettings [ /certificate/settings/get ];\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 \r\ + \n /tool/fetch (\$GlobalScriptsUrl . \"certs/\" . \$CertFileName) dst-p\ + ath=\$CertFileName as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint \ + ] ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n :delay 1s;\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 :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 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 \"\";\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/\");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass {\r\ + \n \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass {\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/368/b69ddb1c\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 09:35:02 01.05.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 138;\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 GlobalEnvRemove;\r\ + \n:global MiniDateTimeStamp;\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 ExitError;\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 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 WaitDNSResolving;\r\ + \n:global WaitFullyConnected;\r\ + \n:global WaitTimeSync;\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 FetchLargeFiles;\r\ + \n:global FetchUserAgentStr;\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 LogPrint;\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n \$LogPrint debug \$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 \$LogPrint debug \$0 (\"Set updates channel to: \$updateChannel\");\ + \r\ + \n }\r\ + \n\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n \$LogPrint debug \$0 (\"Update Channel: \" . \$Channel . \".\");\r\ + \n\r\ + \n\r\ + \n :local StatusPkg \"undefined\";\r\ + \n \$LogPrint debug \$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 \$LogPrint debug \$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 \$LogPrint debug \$0 (\"\\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n \$LogPrint debug \$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\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\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\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\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\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: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: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# !!! The function is not ready !!!\r\ + \n:global FetchHttpInfo do={\r\ + \n :local Mac [ :tostr \$1 ];\r\ + \n :global CertificateAvailable;\r\ + \n :global IsMacLocallyAdministered;\r\ + \n :global LogPrint;\r\ + \n :local Url \"https://api.macvendors.com/\";\r\ + \n :if ([ \$IsMacLocallyAdministered \$Mac ] = true) do={:return \"locall\ + y administered\";}\r\ + \n :do {\r\ + \n :if ([ \$CertificateAvailable \"GTS Root R4\" \"fetch\" ] = false) d\ + o={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Vendor ([ /tool/fetch \r\ + \n (\$Url . [ :pick \$Mac 0 8 ]) output=user as-value ]->\"data\");\r\ + \n :return \$Vendor;\r\ + \n } on-error={\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=yes-without-crl (\$Url) output=none \ + as-value;\r\ + \n \$LogPrint debug \$0 (\"The mac vendor is not known in database.\"\ + );\r\ + \n } do={ \$LogPrint warning \$0 (\"Failed getting mac vendor: \" . \$E\ + rr); }\r\ + \n :return \"unknown vendor\";\r\ + \n }\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# check and download required certificate\r\ + \n# Usage: \$CertificateAvailable \"Common Name\" \"intended use for certi\ + ficate (e.g. \"fetch\", \"tls\", \"all\", \"undefined\")\"\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# download and import certificate\r\ + \n# Usage: \$CertificateDownload \"Common Name\"\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 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 http-header-field=({\ + \_[ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n \"https://mkcert.org/generate/\" http-data=[ :serialize to=json \ + ({ \$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 warning (\$0 . \" Failed downloading certificate with CommonN\ + ame '\" . \$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# name a certificate by its common-name\r\ + \n# Usage: \$CertificateNameByCN \"Common Name\"\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 FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\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 (https://amster.vados.ru/; main/3419/3ee\ + 19780)\");\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 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 GetRandomNumber do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndnum from=0 to=[ \$EitherOr [ :tonum \$1 ] 4294967295 ] ]\ + \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 MiniDateTimeStamp do={\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 :return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\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={:return false}\r\ + \n :local FileVal false;\r\ + \n :do {: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 :global LogPrint;\r\ + \n \$LogPrint debug \$0 (\"Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\$LogPrint debug \$0 (\"... which does not \ + exist.\"); :return true};\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\$LogPrint error \$0 (\"\ + Directory '\" . \$DirName . \"' is not a directory.\"); :return false};\r\ + \n :onerror Err {/file/remove \$DirName} do={\$LogPrint error \$0 (\"Remo\ + ving directory '\" . \$DirName . \"' failed: \" . \$Err); :return false};\ + \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 :global LogPrint;\r\ + \n \$LogPrint debug \$0 (\"Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\$LogPrint debug \$0 (\"... which does not\ + \_exist.\"); :return true}\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n \$LogPrint error \$0 (\"File '\" . \$FileName . \"' is not a file.\"\ + ); :return false}\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\$LogPrint error \$0 (\"Removing file '\" . \$FileName . \"' fai\ + led: \" . \$Err); :return false}\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 :global LogPrint;\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 \$LogPrint info \$0 (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n \$LogPrint 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 \$LogPrint 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 \$LogPrint debug \$0 (\"Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n \$LogPrint 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 \$LogPrint 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 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 [ :len\ + \_\$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 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 LogPrint;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n \$LogPrint 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 \$LogPrint warning \$0 (\"This \" . [ \$IfThenElse ([ :pick \$Call\ + er 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: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: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 LogPrint;\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 \$LogPrint error \$0 (\"A script named '\" . \$Script . \"' does not\ + \_exist!\");\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 \$LogPrint error \$0 (\"More tickets than running scripts '\" . \$Sc\ + ript . \"', 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: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: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:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global LogPrint;\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 \$LogPrint 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: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: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: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:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\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: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# read parts of a large file\r\ + \n:set FetchLargeFiles do={\r\ + \n :local ScriptName [:tostr \$1];\r\ + \n :local fetchUrl [:tostr \$2];\r\ + \n :local CheckCert [:tostr \$3];\r\ + \n :local fetchMode [:tostr \$4];\r\ + \n :local fetchMethod [:tostr \$5];\r\ + \n :local FetchString \"undefined\";\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 #:set fetchMode [ \$IfThenElse (\$fetchMode != \"false\") [:tostr (\"m\ + ode=\" . \$fetchMode)] ];\r\ + \n #:set fetchMethod [\$IfThenElse (\$fetchMethod != \"false\") [:tostr (\ + \"http-method=\\\"\" . \$fetchMethod . \"\\\"\") ]];\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 :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n #/tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName h\ + ttp-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-value;\r\ + \n #:log warning (\"\\\$fUrl =\" . \$2 . ({[ \$fMode ]}));\r\ + \n :onerror Err {\r\ + \n :set FetchString (\"/tool/fetch \" . \\\r\ + \n [\$IfThenElse ([\$fetchMethod] != \"false\") (\"http-method=\\\"\"\ + \_. \$fetchMethod . \"\\\" \")] . \\\r\ + \n \"check-certificate=\" . \$CheckCert . \" url=(\\\"\" . \$fetchUrl\ + \_. \"\\\") \" . \\\r\ + \n [\$IfThenElse ([\$fetchMode] != \"false\") (\"mode=\" . \$fetchMod\ + e . \" \")] . \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue);\r\ + \n :log warning (\"\\\$FetchSring = \" . \$FetchString);\r\ + \n :put [\$FetchString];\r\ + \n #:put [/tool/fetch \$fetchMode check-certificate=\$CheckCert url=(\$\ + fetchUrl) dst-path=(\$FileName) http-header-field=({ [ \$FetchUserAgentStr\ + \_\$ScriptName ] }) as-value];\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 \" . \$fetchUrl . \"\ + \_- \" . \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$fetchUrl . \ + \" did not show up.\");\r\ + \n :return false;\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 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={:set NotificationMessage (\$NotificationMessage . \"\\n\\nNe\ + ws and changes are not available.\")}\r\ + \n\r\ + \n :set GlobalConfChanges;\r\ + \n :set GlobalConfMigration;\r\ + \n }\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n# Global Environment clear. Use: \"\$GlobalEnvRemove True;\".\r\ + \n:set GlobalEnvRemove do={\r\ + \n :local doRmEnv [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ScriptsUrl;\r\ + \n :global LogPrint;\r\ + \n :if (\$doRmEnv != \"True\") do={\r\ + \n \$LogPrint error \"GlobalEnvRemove\" (\"This script is needed \\\"T\ + rue\\\" for confirm to remove global environment variables!\");\r\ + \n :return [];\r\ + \n :error true; \r\ + \n } else={\r\ + \n :set ScriptsUrl \$GlobalScriptsUrl;\r\ + \n :delay 5;\r\ + \n :foreach var in=[/system/script/environment print as-value] do={/sy\ + stem/script/environment remove (\$var->\".id\")}\r\ + \n :delay 5;\r\ + \n :global GlobalScriptsUrl \$ScriptsUrl;\r\ + \n \$LogPrint warning \"GlobalEnvRemove\" (\"Done!\");\r\ + \n :return [];\r\ + \n :error false;\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 \"Setup Global Environment for scripts\";\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#:local Resource [ /system/resource/get ];\r\ + \n#:log info (\$ScriptName . \" Loaded on \" . \$Resource->\"board-name\" \ + . \" with RouterOS \" . \$Resource->\"version\" . \" and Architecture \" .\ + \".\");\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# signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\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} do={:global ExitOnError; \$ExitOnError \$0 \$Err;}\r\ + \n\r\ + \n#} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n# log the successful load of the script, or an error if it failed\r\ + \n#:if ([\$GlobalFuncReady] = true && [\$GlobalConfReady] = true) do={\r\ + \n" +add comment="Example add WU servers to AddrList" dont-require-permissions=no \ + name=WU-add-to-list owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \_Script to add target web sites DNS IP addresses into address list\r\ + \n# Tested with Mikrotik 6.4x\r\ + \n# Syed Jahanzaib / aacable@hotmail.com\r\ + \n# List name\r\ + \n:local LISTNAME \"windowsupdateslist\"\r\ + \n# Web site names which will be added in address list\r\ + \n:local TARGET1 \"update.microsoft.com\"\r\ + \n:local TARGET2 \"download.windowsupdate.com\"\r\ + \n:local TARGET3 \"download.microsoft.com\"\r\ + \n:local TARGET4 \"windowsupdate.com\"\r\ + \n:local TARGET5 \"ntservicepack.microsoft.com\"\r\ + \n:local TARGET6 \"wustat.windows.com\"\r\ + \n:local TARGET7 \"stats.microsoft.com\"\r\ + \n:local TARGET8 \"mp.microsoft.com\"\r\ + \n:local TARGET9 \"ws.microsoft.com\"\r\ + \n:local TARGET10 \"sls.update.microsoft.com.akadns.net\"\r\ + \n:local TARGET11 \"vortex.data.microsoft.com\"\r\ + \n:local TARGET12 \"vortex-win.data.microsoft.com\"\r\ + \n:local TARGET13 \"fe2.update.microsoft.com.akadns.net\"\r\ + \n:local TARGET14 \"statsfe2.update.microsoft.com.akadns.net\"\r\ + \n \r\ + \n# 1st time runner, check if address list is not created previously and a\ + dd entries in it for 1st time usage\r\ + \n:if ( [/ip firewall address-list find where list=\$LISTNAME] = \"\") do=\ + {\r\ + \n:log warning \"No address list for \$TARGET1 found ! creating and adding\ + \_resolved entry for 1st time usage ... zaib\"\r\ + \n \r\ + \n:local FirstAddr [resolve \$TARGET1];\r\ + \n\r\ + \n/ip firewall address-list add address=\$FirstAddr list=\$LISTNAME commen\ + t=\$TARGET1;\r\ + \n} else={\r\ + \n:log warning \"Previous List for \$LISTNAME found ! moving forward and c\ + hecking if DNS entries can be added in it ...\"\r\ + \n}\r\ + \n \r\ + \n# Check DNS entries and collect matching names\r\ + \n:foreach i in=[/ip dns cache find name~\"(\$TARGET1|\$TARGET2|\$TARGET3|\ + \$TARGET4|\$TARGET5|\$TARGET6|\$TARGET7|\$TARGET8|\$TARGET9|\$TARGET10|\$T\ + ARGET11|\$TARGET12|\$TARGET13|\$TARGET14)\" && \$type ~ \"^(A|CNAME)\\\$\"\ + \_] do={\r\ + \n \r\ + \n# Get IP Address from the names and hold it in temporary buffer\r\ + \n:local Buffer [/ip dns cache get \$i data];\r\ + \ndelay delay-time=10ms\r\ + \n \r\ + \n# Check if entry is already not exists, then OK, otherwise ignore duplic\ + ation\r\ + \n:if ( [/ip firewall address-list find where address=\$Buffer] = \"\") do\ + ={\r\ + \n \r\ + \n# Fetch DNS names for the entries\r\ + \n:local sitednsname [/ip dns cache get \$i name] ;\r\ + \n \r\ + \n# Print name in LOG window\r\ + \n:log info (\"added entry: \$sitednsname \$Buffer\");\r\ + \n \r\ + \n# Add IP addresses and there names to the address list\r\ + \n/ip firewall address-list add address=\$Buffer list=\$LISTNAME comment=\ + \$sitednsname;\r\ + \n}\r\ + \n}" +add comment="Add Hosts DNS names to NoWG addres list" \ + dont-require-permissions=no name=Add2vdsina owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \_Script to add target web sites DNS IP addresses into address list\r\ + \n# Tested with Mikrotik 6.4x\r\ + \n# \r\ + \n# List name\r\ + \n:local LISTNAME \"NoWG\"\r\ + \n#\r\ + \n# Web site names which will be added in address list\r\ + \n:local TARGET1 \"comp-lenta.ru\"\r\ + \n:local TARGET2 \"ok.ru\"\r\ + \n:local TARGET3 \"vk.com\"\r\ + \n#:local TARGET4 \"smtp.mail.me.com\"\r\ + \n#:local TARGET4 \"windowsupdate.com\"\r\ + \n#:local TARGET5 \"ntservicepack.microsoft.com\"\r\ + \n#:local TARGET6 \"wustat.windows.com\"\r\ + \n#:local TARGET7 \"stats.microsoft.com\"\r\ + \n#:local TARGET8 \"mp.microsoft.com\"\r\ + \n#:local TARGET9 \"ws.microsoft.com\"\r\ + \n#:local TARGET10 \"sls.update.microsoft.com.akadns.net\"\r\ + \n#:local TARGET11 \"vortex.data.microsoft.com\"\r\ + \n#:local TARGET12 \"vortex-win.data.microsoft.com\"\r\ + \n#:local TARGET13 \"fe2.update.microsoft.com.akadns.net\"\r\ + \n#:local TARGET14 \"statsfe2.update.microsoft.com.akadns.net\"\r\ + \n \r\ + \n# 1st time runner, check if address list is not created previously and a\ + dd entries in it for 1st time usage\r\ + \n:if ( [/ip firewall address-list find where list=\$LISTNAME] = \"\") do=\ + {\r\ + \n:log warning \"No address list for \$TARGET1 found ! creating and adding\ + \_resolved entry for 1st time usage ... zaib\"\r\ + \n\r\ + \n:local FirstAddr [resolve \$TARGET1];\r\ + \n\r\ + \n/ip firewall address-list add address=\$FirstAddr list=\$LISTNAME commen\ + t=\$TARGET1;\r\ + \n\r\ + \n} else={\r\ + \n:log warning \"Previous List for \$LISTNAME found ! moving forward and c\ + hecking if DNS entries can be added in it ...\"\r\ + \n}\r\ + \n\r\ + \n# Check DNS entries and collect matching names\r\ + \n:foreach i in=[/ip dns cache find name~\"(\$TARGET1|\$TARGET2|\$TARGET3|\ + \$TARGET4)\" && \$type ~ \"^(A|CNAME)\\\$\" ] do={\r\ + \n\r\ + \n:log info (\"start entry: \$c \$sitednsname \$Buffer\");\r\ + \n\r\ + \n# Get IP Address from the names and hold it in temporary buffer\r\ + \n:local Buffer [/ip dns cache get \$i data];\r\ + \ndelay delay-time=10ms\r\ + \n:log info (\"1: \$Buffer\"); \r\ + \n\r\ + \n# Check if entry is already not exists, then OK, otherwise ignore duplic\ + ation\r\ + \n:if ( [/ip firewall address-list find where address=\$Buffer] = \"\") do\ + ={\r\ + \n\r\ + \n:log info (\"2: \$Buffer\"); \r\ + \n \r\ + \n# Fetch DNS names for the entries\r\ + \n:local sitednsname [/ip dns cache get \$i name] ;\r\ + \n \r\ + \n# Print name in LOG window\r\ + \n:log info (\"added entry: \$i \$sitednsname \$Buffer\");\r\ + \n \r\ + \n# Add IP addresses and there names to the address list\r\ + \n/ip firewall address-list add address=\$Buffer list=\$LISTNAME comment=\ + \$sitednsname;\r\ + \n}\r\ + \n}" +add comment="Flush DNS Cache" dont-require-permissions=no name=\ + DNS_Flush_Cache owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\ + "/ip dns cache flush" +add dont-require-permissions=no name=create_mkdir_function owner=vados \ + policy=read,write source="# Written by Phillip Stromberg in 2019\r\ + \n# Distributed under the MIT license\r\ + \n# \r\ + \n# \r\ + \n# this creates a function called \$mkdir\r\ + \n# from the command line you will be able to just type \"\$mkdir some/new\ + /folder\"\r\ + \n# and all the folders that need to be created will be\r\ + \n# if the folder already existed, it is ignored\r\ + \n\r\ + \n:log info \"Creating \\\$mkdir function...\"\r\ + \n\r\ + \n:global mkdir do={\r\ + \n # \$1 refers to the input variable the user passed in to us\r\ + \n :local newFolder \$1;\r\ + \n\r\ + \n # we'll use this variable to report error messages that may arise\r\ + \n :local errorMsg;\r\ + \n\r\ + \n # if the user put a slash on the end of the new folder name, remove \ + it\r\ + \n # i.e. \"some/test/\" becomes \"some/test\"\r\ + \n :while (([:pick \$newFolder ([:len \$newFolder] - 1)]) = \"/\") do={\ + \r\ + \n :set newFolder [:pick \$newFolder 0 ([:len \$newFolder] - 1)]\r\ + \n }\r\ + \n\r\ + \n # if the user put a slash at the beginning of the new folder name, r\ + emove it\r\ + \n # i.e. \"/some/test\" becomes \"some/test\"\r\ + \n :while ([:pick \$newFolder 0] = \"/\") do={\r\ + \n :set newFolder [:pick \$newFolder 1 [:len \$newFolder]]\r\ + \n }\r\ + \n\r\ + \n :if ([/file find name=\$newFolder] != \"\") do={\r\ + \n :set errorMsg \"'\$newFolder' already exists.\";\r\ + \n :log debug \"mkdir: \$errorMsg\";\r\ + \n :return \$newFolder;\r\ + \n }\r\ + \n\r\ + \n # the name of the temp file to create\r\ + \n :local tempfile \"mkdir_temp_file.txt\";\r\ + \n\r\ + \n # port 0 is unlikely to be open to HTTP requests...\r\ + \n :local fakeURL \"http://127.0.0.1:0/should-not-exist.txt\";\r\ + \n\r\ + \n :local fullTempPath (\$newFolder . \"/\" . \$tempfile);\r\ + \n\r\ + \n # this is where the folder creation happens\r\ + \n # `as-value` prevents status messages from printing\r\ + \n # wrapping in a `:do {...} on-error` prevents error messages from pr\ + inting.\r\ + \n :do {\r\ + \n /tool fetch dst-path=\"\$fullTempPath\" url=\"\$fakeURL\" durati\ + on=0.001s as-value;\r\ + \n } on-error={}\r\ + \n\r\ + \n # this waits for the temporary dir to show up so there isn't a race \ + \r\ + \n # condition where you call mkdir and then try to do something before\ + \_the \r\ + \n # folder is actually created\r\ + \n :local count 0;\r\ + \n :while ([/file find name=\"\$newFolder\"] = \"\") do={\r\ + \n :if (\$count >= 50) do={\r\ + \n # after 50 * 0.1s delays, it's been 5 seconds, which should \ + be long enough\r\ + \n :set errorMsg \"New folder could not be created at \$newFold\ + er\";\r\ + \n :log error \"mkdir: \$errorMsg\"\r\ + \n :error \$errorMsg\r\ + \n }\r\ + \n :delay 0.1s;\r\ + \n :set count (\$count + 1);\r\ + \n }\r\ + \n\r\ + \n :return \$newFolder;\r\ + \n}\r\ + \n\r\ + \n:log info \"Created function \\\$mkdir\"\r\ + \n" +add comment="Certificates CloudFlare, Quad9 " dont-require-permissions=no \ + name=Update-Certificates owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \_CloudFlare\r\ + \n/tool/fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobalRo\ + otCA.crt.pem;\r\ + \n/certificate/import file-name=DigiCertGlobalRootCA.crt.pem passphrase=\"\ + \";\r\ + \n/tool/ fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobalG\ + 2TLSRSASHA2562020CA1-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/DigiCertGlobal\ + G3TLSECCSHA3842020CA1-1.crt.pem\"\r\ + \n/certificate/import file-name=DigiCertGlobalG3TLSECCSHA3842020CA1-1.crt.\ + pem passphrase=\"\";\r\ + \n\r\ + \n#:global AmScriptsUrl;\r\ + \n#google\r\ + \n#:local CertName \"GTS-Root-R1.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ + \n#:set CertName \"GTS-Root-R4.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \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#:set CertName \"ISRG-Root-X1.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \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#:set CertName \"Starfield-Root-Certificate-Authority-G2.pem\";\r\ + \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ + \r\ + \n#/certificate/import file-name=\$CertName passphrase=\"\";\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=script1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + tool/netwatch/add comment=\"doh-fallback, doh-url=https://security.cloudfl\ + are-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Clou\ + dFlare-doh IPv4 1\" host=1.1.1.1;\r\ + \n/tool/netwatch/add comment=\"doh, doh-url=https://security.cloudflare-dn\ + s.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=CloudFlare\ + -doh IPv4 2\" host=1.0.0.1;\r\ + \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ + /dns.google/dns-query, doh-cert=DigiCert Global Root G2, notify, name=Goog\ + le-doh IPv4 1\" host=8.8.8.8;\r\ + \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ + /dns.google/dns-query, doh-cert=DigiCert Global Root G2, notify, name=Goog\ + le-doh IPv4 2\" host=8.8.4.4;\r\ + \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ + /dns.quad9.net/dns-query, doh-cert=DigiCert Global Root G3, notify, name=C\ + uad9-doh IPv4 1\" host=9.9.9.9;\r\ + \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ + /dns.quad9.net/dns-query, doh-cert=DigiCert Global Root G3, notify, name=C\ + uad9-doh IPv4 2\" host=149.112.112.112;\r\ + \n\r\ + \n/tool/netwatch/add comment=\"dns, notify, name=ns1.unet.ws\" host=91.192\ + .189.2;" +add dont-require-permissions=no name=script2 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n\r\ + \n:global InspectVar;\r\ + \n#:global RouterBoard;\r\ + \n#:do {:set RouterBoard [[ :parse \"/system/routerboard/get\" ]]} on-erro\ + r={}\r\ + \n#:global Resources;\r\ + \n#:do {:set Resources [[ :parse \"/system/resource/get\" ]]} on-error={}\ + \r\ + \n\r\ + \n#:global Updates;\r\ + \n#:do {:set Updates [[ :parse \"/system/package/update/get\" ]]} on-error\ + ={}\r\ + \n\r\ + \n#:global PkgUpdStatus \"undefined\";\r\ + \n#:local Update [/system/package/update/get];\r\ + \n#:retry command={:set PkgUpdStatus (\$Update->\"status\")} delay=200ms m\ + ax=5 on-error={};\r\ + \n\r\ + \n#:log warning [\$InspectVar \$RouterBoard];\r\ + \n#:put [\$InspectVar \$RouterBoard];\r\ + \n\r\ + \n#:retry command={/system/package/update check-for-updates without-paging\ + \_as-value} delay=300ms max=5 on-error={};\r\ + \n\r\ + \n#:global arrDeviceInfo ({});\r\ + \n#:local arrDeviceInfo ({\"section\":{\"key\":\"\"; \"value\":\"\";};});\ + \r\ + \n#:set (\$arrDeviceInfo->\"RouterBoard\") [[ :parse \"/system/routerboard\ + /get\" ]];\r\ + \n#:retry command={:set (\$arrDeviceInfo->\"Updates\") [[ :parse \"/system\ + /package/update/get\" ]]} delay=500ms max=5 on-error={:log error (\"Error\ + \")};\r\ + \n# [:toarray {\"section\"={\"key\"=\"\"; \"value\"=\"\";} }];\r\ + \n# [:toarray \"\"];\r\ + \n:local arrDeviceInfo ({});\r\ + \n\r\ + \n:set (\$arrDeviceInfo->\"devUpdates\") {\"channel\"; \"stable\";};\r\ + \n#:do {:set (arrDeviceInfo->\"Updates\") [[ :parse \"/system/package/upda\ + te/get\" ]]} on-error={:log error (\$error)}\r\ + \n\$InspectVar \$arrDeviceInfo;\r\ + \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" +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BC auto-mac=no mlag-peer-port="ether1 - Unet" \ + name=Bridge-LAN port-cost-mode=short +/interface wireless +# managed by CAPsMAN +# channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] ampdu-priorities=0,1,2,3,4,5,6,7 band=\ + 2ghz-b/g/n basic-rates-a/g=\ + 6Mbps,9Mbps,12Mbps,18Mbps,24Mbps,36Mbps,48Mbps,54Mbps basic-rates-b=\ + 1Mbps,2Mbps,5.5Mbps,11Mbps channel-width=20/40mhz-XX country=belarus \ + disabled=no frequency=auto hw-protection-mode=rts-cts mode=ap-bridge \ + rate-set=configured skip-dfs-channels=10min-cac ssid=vados \ + wds-default-bridge=Bridge-LAN wds-mode=dynamic-mesh wireless-protocol=\ + 802.11 wmm-support=enabled wps-mode=disabled +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] ampdu-priorities=0,1,2,3,4,5,6,7 band=\ + 5ghz-a/n/ac basic-rates-a/g=\ + 6Mbps,9Mbps,12Mbps,18Mbps,24Mbps,36Mbps,48Mbps,54Mbps channel-width=\ + 20/40/80mhz-XXXX country=belarus disabled=no frequency=auto \ + hw-protection-mode=rts-cts mode=ap-bridge rate-set=configured \ + skip-dfs-channels=10min-cac ssid=vados5g wds-default-bridge=Bridge-LAN \ + wds-mode=dynamic-mesh wireless-protocol=802.11 wmm-support=enabled \ + wps-mode=disabled +/interface wireless nstreme +# managed by CAPsMAN +# channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +set wlan1 enable-nstreme=yes +# managed by CAPsMAN +# channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set wlan2 enable-nstreme=yes +/caps-man manager +set enabled=yes +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=gn master-configuration=\ + cfg-2ghz name-format=prefix-identity name-prefix=2ghz +add action=create-dynamic-enabled hw-supported-modes=ac master-configuration=\ + cfg-5ghz-ac name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled hw-supported-modes=an master-configuration=\ + cfg-5ghz-an name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set builtin-trust-store="ipsec,wpa-eap,capsman,fetch,sstp,ovpn,radius,userman,\ + openflow,tr069,dot1x,www,api,reverse-proxy" crl-store=system +/interface bridge port +add bridge=Bridge-LAN disabled=yes interface="ether1 - Unet" +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=ether4 +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/ip firewall connection tracking +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set udp-timeout=10s +/ip neighbor discovery-settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set discover-interface-list=LAN +/ip settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set max-neighbor-entries=8192 secure-redirects=no +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ + forward=no max-neighbor-entries=8192 +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=WAN lan-interface-list=\ + LAN wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface="ether1 - Unet" list=WAN +add interface=Bridge-LAN list=LAN +/interface ovpn-server server +add auth=sha1,md5 mac-address=FE:82:A5:78:8E:84 name=ovpn-server1 +/interface wifi configuration +add channel=2G-AX datapath=xs-DataPath disabled=no mode=ap name=2G-AX \ + security=sec2g security.ft=yes .ft-over-ds=yes ssid=vados +add channel=2G-N datapath=xs-DataPath disabled=no mode=ap name=2G-N security=\ + sec2g security.ft=yes .ft-over-ds=yes ssid=vados +add channel=5G-AC datapath=xs-DataPath disabled=no mode=ap name=5G-AC \ + security=sec5G security.authentication-types="" .ft=yes .ft-over-ds=yes \ + ssid=vados +add channel=2G-G datapath=xs-DataPath disabled=no mode=ap name=2G-G security=\ + sec2g security.authentication-types="" .ft=yes .ft-over-ds=yes ssid=vados +/interface wifi datapath +add bridge=Bridge-LAN disabled=no interface-list=all name=xs-DataPath +/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=ether2 enabled=yes interfaces=\ + wlan1,wlan2 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +/ip dhcp-client +add interface="ether1 - Unet" name=client1 +/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 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="=\"Client ID\"" mac-address=DE:4D:72:F3:1A:9F server=\ + masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ + 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=8.8.8.8,8.8.4.4 verify-doh-cert=yes +/ip dns static +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 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 name=reg.vados.ru type=A +add address=10.30.30.33 match-subdomain=yes name=mikrots.vados.ru type=A +add address=10.30.30.33 name=oxidized.vados.ru type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" name=dns.google 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" name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" 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" name=dns.quad9.net \ + type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 name=MikroYellow type=A +add address=10.30.30.85 name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=10.30.30.20 name=garage type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws 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=10.30.30.100-255 list=KIDS +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 +add address=157.240.247.174 list=Instagram +add address=46.53.178.107 list=Instagram +add address=179.60.195.174 list=Instagram +add address=157.240.205.174 list=Instagram +add address=31.13.24.0/21 list=Instagram +add address=31.13.64.0/18 list=Instagram +add address=45.64.40.0/22 list=Instagram +add address=66.220.144.0/20 list=Instagram +add address=69.63.176.0/20 list=Instagram +add address=69.171.224.0/19 list=Instagram +add address=74.119.76.0/22 list=Instagram +add address=103.4.96.0/22 list=Instagram +add address=129.134.0.0/16 list=Instagram +add address=157.240.0.0/16 list=Instagram +add address=173.252.64.0/18 list=Instagram +add address=179.60.192.0/22 list=Instagram +add address=185.60.216.0/22 list=Instagram +add address=204.15.20.0/22 list=Instagram +add address=157.240.200.63 list=Instagram +add address=185.60.219.63 list=Instagram +add address=129.134.31.12 list=Instagram +add address=66.81.203.132 list=Instagram +add address=185.89.218.12 list=Instagram +add address=31.13.66.63 list=Instagram +add address=84.15.65.162 list=Instagram +add address=68.66.224.28 list=Instagram +add address=157.240.253.63 list=Instagram +add address=83.174.11.224 list=Instagram +add address=157.240.9.52 list=Instagram +add address=157.240.252.174 list=Instagram +add address=157.240.195.63 list=Instagram +add address=31.13.71.52 list=Instagram +add address=57.144.110.192 list=Instagram +add address=157.240.252.17 list=Instagram +add address=84.15.66.97 list=Instagram +add address=217.168.6.33 list=Instagram +add address=31.13.83.52 list=Instagram +add address=157.240.241.63 list=Instagram +add address=129.134.30.12 list=Instagram +add address=185.89.219.12 list=Instagram +add address=157.240.252.10 list=Instagram +add address=157.240.201.63 list=Instagram +add address=66.81.203.197 list=Instagram +add address=179.60.195.52 list=Instagram +add address=66.81.203.7 list=Instagram +add address=216.40.34.41 list=Instagram +add address=157.240.202.63 list=Instagram +add address=157.240.229.63 list=Instagram +add address=157.240.252.63 list=Instagram +add address=31.13.72.53 list=Instagram +add address=124.108.16.224 list=Instagram +add address=157.240.205.63 list=Instagram +add address=92.46.37.96 list=Instagram +add address=157.240.247.63 list=Instagram +add address=157.240.234.63 list=Instagram +add address=157.240.235.63 list=Instagram +add address=87.245.208.97 list=Instagram +add address=216.58.192.0/19 list=Instagram +add address=209.85.128.0/17 list=Instagram +add address=198.105.240.0/20 list=Instagram +add address=173.194.0.0/16 list=Instagram +add address=172.217.0.0/16 list=Instagram +add address=142.250.0.0/15 list=Instagram +add address=108.177.0.0/17 list=Instagram +add address=87.245.197.140 list=Instagram +add address=74.125.0.0/16 list=Instagram +add address=64.233.160.0/19 list=Instagram +add address=157.240.0.1 list=Instagram +add address=157.240.238.63 list=Instagram +add address=157.240.238.174 list=Instagram +add address=157.240.0.63 list=Instagram +add address=157.240.224.63 list=Instagram +add address=157.240.224.174 list=Instagram +add address=157.240.251.36 list=Instagram +add address=157.240.253.12 list=Instagram +add address=157.240.253.35 list=Instagram +add address=157.240.238.13 list=Instagram +add address=157.240.238.56 list=Instagram +add address=157.240.238.175 list=Instagram +add address=57.144.112.141 list=Instagram +add address=157.240.251.60 list=Instagram +add address=157.240.251.128 list=Instagram +add address=157.240.238.5 list=Instagram +add address=157.240.253.13 list=Instagram +add address=157.240.253.5 list=Instagram +add address=157.240.238.2 list=Instagram +add address=157.240.238.37 list=Instagram +add address=157.240.251.5 list=Instagram +add address=157.240.251.34 list=Instagram +add address=57.144.112.1 list=Instagram +add address=157.240.238.54 list=Instagram +add address=129.134.26.123 list=Instagram +add address=157.240.252.3 list=Instagram +add address=31.13.84.4 list=Instagram +add address=157.240.224.12 list=Instagram +add address=157.240.238.4 list=Instagram +add address=157.240.0.13 list=Instagram +add address=3.33.139.32 list=Instagram +add address=157.240.0.35 list=Instagram +add address=157.240.238.14 list=Instagram +add address=157.240.238.60 list=Instagram +add address=57.144.112.145 list=Instagram +add address=157.240.251.35 list=Instagram +add address=151.101.84.159 list=Twitter +add address=pbs.twimg.com list=Twitter +add address=89.110.101.17 comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" 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 +add address=geoip.co list=NoVdsina +add address=x.com list=Twitter +add address=facebook.com list=facebook +add address=fbcd.net list=facebook +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=82.202.162.184 comment=comp-lenta.ru list=NoWG +add address=smtp.mail.me.com.akadns.net. comment=smtp.mail.me.com list=NoWG +add address=17.57.155.37 comment=smtp.mail.me.com.akadns.net list=NoWG +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=NUC list=NoWG +add address=10.30.30.10 comment=MikroNUC list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! list=NoWG +add address=89.110.101.17 list=VPN-Server +add address=80.85.244.9 list=TRUSTED +add address=8.8.8.8 list=TRUSTED +add address=8.8.4.4 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 +/ip firewall filter +add action=accept chain=input protocol=icmp +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related +add action=drop chain=input in-interface-list=!LAN +add action=accept chain=input comment=\ + "accept NTP clients on local area network" dst-port=123 in-interface=\ + Bridge-LAN in-interface-list=LAN protocol=udp +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +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=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +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 ICMP" disabled=yes \ + protocol=icmp +add action=accept chain=input comment=\ + "defconf: accept to local loopback (for CAPsMAN)" disabled=yes \ + dst-address=127.0.0.1 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + 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 +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +add action=drop chain=input disabled=yes in-interface-list=!LAN +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 comment=\ + "accept NTP clients on local area network" dst-port=123 in-interface=\ + Bridge-LAN in-interface-list=LAN protocol=udp +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +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=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +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 ICMP" disabled=yes \ + protocol=icmp +add action=accept chain=input comment=\ + "defconf: accept to local loopback (for CAPsMAN)" disabled=yes \ + dst-address=127.0.0.1 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + 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 +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +add action=drop chain=input disabled=yes in-interface-list=!LAN +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 +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Test sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test new-routing-mark=To-VPN \ + passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=Instagram dst-address=\ + !10.30.30.0/24 dst-address-list=Instagram new-routing-mark=To-VPN \ + src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-routing chain=prerouting comment=Twitter dst-address=\ + !10.30.30.0/24 dst-address-list=Twitter new-routing-mark=To-VPN \ + src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-routing chain=prerouting comment=YouTube dst-address=\ + !10.30.30.0/24 dst-address-list=YouTube new-routing-mark=To-VPN \ + src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-routing chain=prerouting comment=facebook dst-address=\ + !10.30.30.0/24 dst-address-list=facebook new-routing-mark=To-VPN \ + 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 log-prefix=VPN-all \ + new-routing-mark=To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 dst-address-list=!NoVdsina log=yes log-prefix=VPN-Net \ + new-routing-mark=To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="Test sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test new-routing-mark=To-VPN \ + passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=Instagram dst-address=\ + !10.30.30.0/24 dst-address-list=Instagram new-routing-mark=To-VPN \ + src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-routing chain=prerouting comment=Twitter dst-address=\ + !10.30.30.0/24 dst-address-list=Twitter new-routing-mark=To-VPN \ + src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-routing chain=prerouting comment=YouTube dst-address=\ + !10.30.30.0/24 dst-address-list=YouTube new-routing-mark=To-VPN \ + src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-routing chain=prerouting comment=facebook dst-address=\ + !10.30.30.0/24 dst-address-list=facebook new-routing-mark=To-VPN \ + 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 log-prefix=VPN-all \ + new-routing-mark=To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 dst-address-list=!NoVdsina log=yes log-prefix=VPN-Net \ + new-routing-mark=To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +/ip firewall nat +add action=masquerade chain=srcnat out-interface-list=WAN +add action=redirect chain=dstnat comment=\ + "FORCE DNS TO LOCAL MIKROTIK DNS SERVER" dst-port=53 protocol=tcp \ + src-address=10.30.30.0/24 to-ports=53 +add action=redirect chain=dstnat dst-port=53 protocol=udp src-address=\ + 10.30.30.0/24 to-ports=53 +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 +add action=redirect chain=dstnat comment=\ + "FORCE DNS TO LOCAL MIKROTIK DNS SERVER" dst-port=53 protocol=tcp \ + src-address=10.30.30.0/24 to-ports=53 +add action=redirect chain=dstnat dst-port=53 protocol=udp src-address=\ + 10.30.30.0/24 to-ports=53 +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=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=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=vk.com \ + content=vk.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=ok.ru content=\ + ok.ru +add action=add-dst-to-address-list address-list=Instagram \ + address-list-timeout=none-dynamic chain=prerouting comment=Instagram \ + content=instagram.com +add action=add-dst-to-address-list address-list=Twitter address-list-timeout=\ + none-dynamic chain=prerouting comment=Twitter content=twimg.com +add action=add-dst-to-address-list address-list=Twitter address-list-timeout=\ + none-dynamic chain=prerouting content=x.com +add action=add-dst-to-address-list address-list=Twitter address-list-timeout=\ + none-dynamic chain=prerouting content=twitter.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting comment=YouTube content=youtube.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=googleapis.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=ggpht.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=youtu.be +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=ytimg.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=googlevideo.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=gstatic.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=googleusercontent.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=nhacmp3youtube.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=me.com \ + content=me.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=icloud.com \ + content=icloud.com +add action=add-dst-to-address-list address-list=facebook \ + address-list-timeout=none-dynamic chain=prerouting comment=facebook \ + content=fbcd.net +add action=add-dst-to-address-list address-list=facebook \ + address-list-timeout=none-dynamic chain=prerouting content=facebook.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=comp-lenta.ru \ + content=comp-lenta.ru +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + smtp.mail.me.com content=smtp.mail.me.com log=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gov.ru \ + content=gov.ru +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + r78.fssp.gov.ru content=r78.fssp.gov.ru +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=api.db-ip.com \ + content=api.db-ip.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + api.telegram.org content=api.telegram.org +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=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=vk.com \ + content=vk.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=ok.ru content=\ + ok.ru +add action=add-dst-to-address-list address-list=Instagram \ + address-list-timeout=none-dynamic chain=prerouting comment=Instagram \ + content=instagram.com +add action=add-dst-to-address-list address-list=Twitter address-list-timeout=\ + none-dynamic chain=prerouting comment=Twitter content=twimg.com +add action=add-dst-to-address-list address-list=Twitter address-list-timeout=\ + none-dynamic chain=prerouting content=x.com +add action=add-dst-to-address-list address-list=Twitter address-list-timeout=\ + none-dynamic chain=prerouting content=twitter.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting comment=YouTube content=youtube.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=googleapis.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=ggpht.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=youtu.be +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=ytimg.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=googlevideo.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=gstatic.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=googleusercontent.com +add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ + none-dynamic chain=prerouting content=nhacmp3youtube.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=me.com \ + content=me.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=icloud.com \ + content=icloud.com +add action=add-dst-to-address-list address-list=facebook \ + address-list-timeout=none-dynamic chain=prerouting comment=facebook \ + content=fbcd.net +add action=add-dst-to-address-list address-list=facebook \ + address-list-timeout=none-dynamic chain=prerouting content=facebook.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=comp-lenta.ru \ + content=comp-lenta.ru +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + smtp.mail.me.com content=smtp.mail.me.com log=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gov.ru \ + content=gov.ru +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + r78.fssp.gov.ru content=r78.fssp.gov.ru +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=api.db-ip.com \ + content=api.db-ip.com +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + api.telegram.org content=api.telegram.org +/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 user profile +set [ find default=yes ] address-pool=dhcp +/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 disabled=yes dst-address=0.0.0.0/0 gateway=10.30.30.81 +add disabled=yes distance=2 dst-address=0.0.0.0/0 gateway=146.120.64.209 \ + routing-table=main scope=30 target-scope=10 +add disabled=no distance=2 dst-address=0.0.0.0/0 gateway=146.120.64.209 \ + routing-table=main 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 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 interface="ether1 - Unet" type=external +/snmp community +set [ find default=yes ] addresses=10.30.30.0/24 read-access=no security=\ + private +/system clock +set time-zone-autodetect=no 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 scheduler +add comment="Setup Global Environment for scripts" 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="Windows Update script" interval=2d5m name=WU-script-run-schedule \ + on-event=WU-add-to-list policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2014-02-11 start-time=00:00:00 +add comment="Backup Every Week" disabled=yes interval=1w name=BackupEveryWeek \ + on-event=BackupAndUpdate policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2025-07-07 start-time=03:41:27 +add comment="DoH Cache Flush" interval=1d name="DNS Cache Flush" on-event=\ + DNS_Flush_Cache policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2019-12-06 start-time=05:30:00 +add comment="Create mkdir function on startup" name=\ + create_mkdir_function_on_startup on-event=\ + "/system script run create_mkdir_function;" policy=read,write,policy,test \ + start-time=startup +add comment="Scheduler for Update Certificates CloudFlare Quad9" interval=1w \ + name="Update Certificates CloudFlare Quad9" on-event=\ + "/system/script { run Update-Certificates; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2025-11-28 start-time=05:45:40 +add comment="Scheduler for Amster-Backup-UpdateEmail" disabled=yes interval=\ + 1w name=Amster-Backup-UpdateEmail on-event=\ + "/system/script { run Amster-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-02-13 start-time=03:41:27 +/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 comment="doh, doh-url=https://security.cloudflare-dns.com/dns-query, notif\ + y, name=CloudFlare-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 \ + http-codes="" test-script="" type=simple 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="" \ + port=443 test-script="" type=simple up-script="" +add comment="dns-fallback, notify, name=Google-DNS IPv4" disabled=yes \ + down-script="" host=8.8.8.8 http-codes="" test-script="" type=simple \ + up-script="" +add comment="doh-fallback, doh-url=https://dns.google/dns-query, doh-cert=GTS \ + Root R1, notify, name=Google-DOH IPv4" disabled=yes down-script="" host=\ + 8.8.4.4 http-codes="" test-script="" type=simple up-script="" +add comment="dns, notify, name=Cuad9-DOH IPv4" disabled=yes down-script="" \ + host=9.9.9.9 http-codes="" test-script="" type=simple up-script="" +add comment="doh-fallback, doh-url=https://dns.quad9.net/dns-query, name=Cuad9\ + -DOH IPv4" disabled=yes down-script="" host=149.112.112.112 http-codes="" \ + test-script="" type=simple up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" test-script="" type=simple \ + up-script="" +/tool sniffer +set filter-interface=Bridge-LAN filter-src-ip-address=10.30.30.50/32 +/user aaa +set accounting=no diff --git a/MikroMaster/history/20260503/20260503162218-Diff-Config.rsc b/MikroMaster/history/20260503/20260503162218-Diff-Config.rsc new file mode 100644 index 0000000..9335d42 --- /dev/null +++ b/MikroMaster/history/20260503/20260503162218-Diff-Config.rsc @@ -0,0 +1,3747 @@ +1,2c1,2 +< # 2026-05-03 16:22:19 by RouterOS 7.22.1 +< # software id = A8L5-07YF +--- +> # 2026-05-03 13:27:06 by RouterOS 7.22 +> # software id = 2961-C4ZZ +4,15c4,8 +< # model = RB952Ui-5ac2nD +< # serial number = D3D50CEDCD38 +< /caps-man configuration +< add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ +< XX country=belarus datapath.client-to-client-forwarding=yes \ +< .local-forwarding=yes name=cfg-2ghz ssid=vados +< add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ +< XXXX country=belarus datapath.client-to-client-forwarding=yes \ +< .local-forwarding=yes name=cfg-5ghz-ac ssid=vados5g +< add channel.band=5ghz-a/n .control-channel-width=20mhz .extension-channel=XX \ +< country=belarus datapath.client-to-client-forwarding=yes \ +< .local-forwarding=yes name=cfg-5ghz-an ssid=vados5g +--- +> # model = RBD52G-5HacD2HnD +> # serial number = HCX084RAVFA +> /interface bridge +> add admin-mac=48:8F:5A:60:A1:0B auto-mac=no name=Bridge-LAN port-cost-mode=\ +> short +18c11 +< 48:8F:5A:8E:1A:C2 name="ether1 - Unet" +--- +> F6:BE:9D:85:53:F5 name="ether1 - Unet" +28a22,43 +> /interface wireless +> # managed by CAPsMAN +> # channel: 2422/20-Ce/gn(17dBm), SSID: vados, local forwarding +> set [ find default-name=wlan1 ] ampdu-priorities=0,1,2,3,4,5,6,7 band=\ +> 2ghz-b/g/n basic-rates-a/g=\ +> 6Mbps,9Mbps,12Mbps,18Mbps,24Mbps,36Mbps,48Mbps,54Mbps basic-rates-b=\ +> 1Mbps,2Mbps,5.5Mbps,11Mbps channel-width=20/40mhz-XX country=belarus \ +> disabled=no frequency=auto hw-protection-mode=rts-cts mode=ap-bridge \ +> rate-set=configured skip-dfs-channels=10min-cac ssid=vados \ +> station-roaming=enabled wds-default-bridge=Bridge-LAN wds-mode=\ +> dynamic-mesh wireless-protocol=802.11 wmm-support=enabled wps-mode=\ +> disabled +> # managed by CAPsMAN +> # channel: 5200/20-eCee/ac/P(20dBm), SSID: vados5g, local forwarding +> set [ find default-name=wlan2 ] ampdu-priorities=0,1,2,3,4,5,6,7 band=\ +> 5ghz-a/n/ac basic-rates-a/g=\ +> 6Mbps,9Mbps,12Mbps,18Mbps,24Mbps,36Mbps,48Mbps,54Mbps channel-width=\ +> 20/40/80mhz-XXXX country=belarus disabled=no frequency=auto \ +> hw-protection-mode=rts-cts mode=ap-bridge rate-set=configured \ +> skip-dfs-channels=10min-cac ssid=vados5g station-roaming=enabled \ +> wds-default-bridge=Bridge-LAN wds-mode=dynamic-mesh wireless-protocol=\ +> 802.11 wmm-support=enabled wps-mode=disabled +30a46,65 +> /interface wireless nstreme +> # managed by CAPsMAN +> # channel: 2422/20-Ce/gn(17dBm), SSID: vados, local forwarding +> set wlan1 enable-nstreme=yes +> # managed by CAPsMAN +> # channel: 5200/20-eCee/ac/P(20dBm), SSID: vados5g, local forwarding +> set wlan2 enable-nstreme=yes +> /caps-man security +> add authentication-types=wpa-psk,wpa2-psk encryption=aes-ccm \ +> group-encryption=aes-ccm name=security +> /caps-man configuration +> add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ +> XX country=belarus datapath.client-to-client-forwarding=yes \ +> .local-forwarding=yes name=cfg-2ghz security=security ssid=vados +> add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ +> XXXX country=belarus datapath.client-to-client-forwarding=yes \ +> .local-forwarding=yes name=cfg-5ghz-ac security=security ssid=vados5g +> add channel.band=5ghz-a/n .control-channel-width=20mhz .extension-channel=XX \ +> country=belarus datapath.client-to-client-forwarding=yes \ +> .local-forwarding=yes name=cfg-5ghz-an security=security ssid=vados5g +32c67,68 +< add slot=tmpfs tmpfs-max-size=22369621 type=tmpfs +--- +> add media-interface=lo slot=tmpfs tmpfs-max-size=44739242 type=tmpfs +> set usb1 mount-point-template="[tmpfs]" +34,35c70,71 +< add name=WAN +< add name=LAN +--- +> add comment=defconf name=WAN +> add comment=defconf name=LAN +44a81,82 +> /interface wifi datapath +> add bridge=Bridge-LAN disabled=no interface-list=all name=xs-DataPath +51a90,93 +> add channel=2G-AX datapath=xs-DataPath disabled=no mode=ap name=2G-AX \ +> security=sec2g security.ft=yes .ft-over-ds=yes ssid=vados +> add channel=2G-N datapath=xs-DataPath disabled=no mode=ap name=2G-N security=\ +> sec2g security.ft=yes .ft-over-ds=yes ssid=vados +53a96,100 +> add channel=5G-AC datapath=xs-DataPath disabled=no mode=ap name=5G-AC \ +> security=sec5G security.authentication-types="" .ft=yes .ft-over-ds=yes \ +> ssid=vados +> add channel=2G-G datapath=xs-DataPath disabled=no mode=ap name=2G-G security=\ +> sec2g security.authentication-types="" .ft=yes .ft-over-ds=yes ssid=vados +56c103,104 +< set [ find default=yes ] supplicant-identity=MikroTik +--- +> set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ +> dynamic-keys supplicant-identity=MikroTik +74a123,124 +> add name=mVados options=HostName +> add name=hostname-search-set options=HostName +76c126 +< add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +--- +> add comment=BS dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +82a133,135 +> add dns-servers=1.0.0.1,1.1.1.1 name=m.blsp.at verify-doh-cert=no +> add comment=telegram disabled=yes dns-servers=10.30.30.81 name=\ +> api.telegram.org verify-doh-cert=no +86c139,146 +< add name=dhcp ranges=10.30.30.83-10.30.30.254 +--- +> add name=dhcp ranges=10.30.30.150-10.30.30.200 +> /ip dhcp-server +> add add-arp=yes address-pool=dhcp dhcp-option-set=domain-search-set \ +> dynamic-lease-identifiers=client-mac,client-id,opt-82 interface=\ +> Bridge-LAN lease-time=10m name=masterDHCP server-address=10.30.30.81 \ +> use-reconfigure=yes +> /ip hotspot user profile +> set [ find default=yes ] address-pool=dhcp +95a156,158 +> /snmp community +> set [ find default=yes ] addresses=10.30.30.0/24 read-access=no security=\ +> private +99c162,163 +< add dont-require-permissions=no name=fetchCustom owner=vados policy=\ +--- +> add comment="Example add WU servers to AddrList" dont-require-permissions=no \ +> name=WU-add-to-list owner=vados policy=\ +101,133c165,538 +< /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#:local Script AM-setup\ +< \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\");\ +< \n#/usb1/mikro_bak_rsa.pub\ +< \n\ +< \n#\ +< \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#\ +< \n#root.mnuc.backuper\ +< \n\ +< \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ +< 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ +< \n#:local Script AM-GlobalConfig;\ +< \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\");\ +< \n#/usb1/mikro_bak_rsa.pub\ +< \n#:local FetchFile AM-DOHCert.rsc\ +< \n\ +< \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\ +< \n:global MkDir;\ +< \n\ +< \n:local FetchFile Binary.backup\ +< \n:local localDir \"\";\ +< \n\ +< \n#\$MkDir \$localDir;\ +< \n\ +< \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ +< \$localDir . \$FetchFile);" +--- +> \_Script to add target web sites DNS IP addresses into address list\r\ +> \n# Tested with Mikrotik 6.4x\r\ +> \n# Syed Jahanzaib / aacable@hotmail.com\r\ +> \n# List name\r\ +> \n:local LISTNAME \"windowsupdateslist\"\r\ +> \n# Web site names which will be added in address list\r\ +> \n:local TARGET1 \"update.microsoft.com\"\r\ +> \n:local TARGET2 \"download.windowsupdate.com\"\r\ +> \n:local TARGET3 \"download.microsoft.com\"\r\ +> \n:local TARGET4 \"windowsupdate.com\"\r\ +> \n:local TARGET5 \"ntservicepack.microsoft.com\"\r\ +> \n:local TARGET6 \"wustat.windows.com\"\r\ +> \n:local TARGET7 \"stats.microsoft.com\"\r\ +> \n:local TARGET8 \"mp.microsoft.com\"\r\ +> \n:local TARGET9 \"ws.microsoft.com\"\r\ +> \n:local TARGET10 \"sls.update.microsoft.com.akadns.net\"\r\ +> \n:local TARGET11 \"vortex.data.microsoft.com\"\r\ +> \n:local TARGET12 \"vortex-win.data.microsoft.com\"\r\ +> \n:local TARGET13 \"fe2.update.microsoft.com.akadns.net\"\r\ +> \n:local TARGET14 \"statsfe2.update.microsoft.com.akadns.net\"\r\ +> \n \r\ +> \n# 1st time runner, check if address list is not created previously and a\ +> dd entries in it for 1st time usage\r\ +> \n:if ( [/ip firewall address-list find where list=\$LISTNAME] = \"\") do=\ +> {\r\ +> \n:log warning \"No address list for \$TARGET1 found ! creating and adding\ +> \_resolved entry for 1st time usage ... zaib\"\r\ +> \n \r\ +> \n:local FirstAddr [resolve \$TARGET1];\r\ +> \n\r\ +> \n/ip firewall address-list add address=\$FirstAddr list=\$LISTNAME commen\ +> t=\$TARGET1;\r\ +> \n} else={\r\ +> \n:log warning \"Previous List for \$LISTNAME found ! moving forward and c\ +> hecking if DNS entries can be added in it ...\"\r\ +> \n}\r\ +> \n \r\ +> \n# Check DNS entries and collect matching names\r\ +> \n:foreach i in=[/ip dns cache find name~\"(\$TARGET1|\$TARGET2|\$TARGET3|\ +> \$TARGET4|\$TARGET5|\$TARGET6|\$TARGET7|\$TARGET8|\$TARGET9|\$TARGET10|\$T\ +> ARGET11|\$TARGET12|\$TARGET13|\$TARGET14)\" && \$type ~ \"^(A|CNAME)\\\$\"\ +> \_] do={\r\ +> \n \r\ +> \n# Get IP Address from the names and hold it in temporary buffer\r\ +> \n:local Buffer [/ip dns cache get \$i data];\r\ +> \ndelay delay-time=10ms\r\ +> \n \r\ +> \n# Check if entry is already not exists, then OK, otherwise ignore duplic\ +> ation\r\ +> \n:if ( [/ip firewall address-list find where address=\$Buffer] = \"\") do\ +> ={\r\ +> \n \r\ +> \n# Fetch DNS names for the entries\r\ +> \n:local sitednsname [/ip dns cache get \$i name] ;\r\ +> \n \r\ +> \n# Print name in LOG window\r\ +> \n:log info (\"added entry: \$sitednsname \$Buffer\");\r\ +> \n \r\ +> \n# Add IP addresses and there names to the address list\r\ +> \n/ip firewall address-list add address=\$Buffer list=\$LISTNAME comment=\ +> \$sitednsname;\r\ +> \n}\r\ +> \n}" +> add comment="Add Hosts DNS names to NoWG addres list" \ +> dont-require-permissions=no name=Add2vdsina owner=vados policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ +> \_Script to add target web sites DNS IP addresses into address list\r\ +> \n# Tested with Mikrotik 6.4x\r\ +> \n# \r\ +> \n# List name\r\ +> \n:local LISTNAME \"NoWG\"\r\ +> \n#\r\ +> \n# Web site names which will be added in address list\r\ +> \n:local TARGET1 \"comp-lenta.ru\"\r\ +> \n:local TARGET2 \"ok.ru\"\r\ +> \n:local TARGET3 \"vk.com\"\r\ +> \n#:local TARGET4 \"smtp.mail.me.com\"\r\ +> \n#:local TARGET4 \"windowsupdate.com\"\r\ +> \n#:local TARGET5 \"ntservicepack.microsoft.com\"\r\ +> \n#:local TARGET6 \"wustat.windows.com\"\r\ +> \n#:local TARGET7 \"stats.microsoft.com\"\r\ +> \n#:local TARGET8 \"mp.microsoft.com\"\r\ +> \n#:local TARGET9 \"ws.microsoft.com\"\r\ +> \n#:local TARGET10 \"sls.update.microsoft.com.akadns.net\"\r\ +> \n#:local TARGET11 \"vortex.data.microsoft.com\"\r\ +> \n#:local TARGET12 \"vortex-win.data.microsoft.com\"\r\ +> \n#:local TARGET13 \"fe2.update.microsoft.com.akadns.net\"\r\ +> \n#:local TARGET14 \"statsfe2.update.microsoft.com.akadns.net\"\r\ +> \n \r\ +> \n# 1st time runner, check if address list is not created previously and a\ +> dd entries in it for 1st time usage\r\ +> \n:if ( [/ip firewall address-list find where list=\$LISTNAME] = \"\") do=\ +> {\r\ +> \n:log warning \"No address list for \$TARGET1 found ! creating and adding\ +> \_resolved entry for 1st time usage ... zaib\"\r\ +> \n\r\ +> \n:local FirstAddr [resolve \$TARGET1];\r\ +> \n\r\ +> \n/ip firewall address-list add address=\$FirstAddr list=\$LISTNAME commen\ +> t=\$TARGET1;\r\ +> \n\r\ +> \n} else={\r\ +> \n:log warning \"Previous List for \$LISTNAME found ! moving forward and c\ +> hecking if DNS entries can be added in it ...\"\r\ +> \n}\r\ +> \n\r\ +> \n# Check DNS entries and collect matching names\r\ +> \n:foreach i in=[/ip dns cache find name~\"(\$TARGET1|\$TARGET2|\$TARGET3|\ +> \$TARGET4)\" && \$type ~ \"^(A|CNAME)\\\$\" ] do={\r\ +> \n\r\ +> \n:log info (\"start entry: \$c \$sitednsname \$Buffer\");\r\ +> \n\r\ +> \n# Get IP Address from the names and hold it in temporary buffer\r\ +> \n:local Buffer [/ip dns cache get \$i data];\r\ +> \ndelay delay-time=10ms\r\ +> \n:log info (\"1: \$Buffer\"); \r\ +> \n\r\ +> \n# Check if entry is already not exists, then OK, otherwise ignore duplic\ +> ation\r\ +> \n:if ( [/ip firewall address-list find where address=\$Buffer] = \"\") do\ +> ={\r\ +> \n\r\ +> \n:log info (\"2: \$Buffer\"); \r\ +> \n \r\ +> \n# Fetch DNS names for the entries\r\ +> \n:local sitednsname [/ip dns cache get \$i name] ;\r\ +> \n \r\ +> \n# Print name in LOG window\r\ +> \n:log info (\"added entry: \$i \$sitednsname \$Buffer\");\r\ +> \n \r\ +> \n# Add IP addresses and there names to the address list\r\ +> \n/ip firewall address-list add address=\$Buffer list=\$LISTNAME comment=\ +> \$sitednsname;\r\ +> \n}\r\ +> \n}" +> add comment="Flush DNS Cache" dont-require-permissions=no name=\ +> DNS_Flush_Cache owner=vados policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\ +> "/ip dns cache flush" +> add dont-require-permissions=no name=create_mkdir_function owner=vados \ +> policy=read,write source="# Written by Phillip Stromberg in 2019\r\ +> \n# Distributed under the MIT license\r\ +> \n# \r\ +> \n# \r\ +> \n# this creates a function called \$mkdir\r\ +> \n# from the command line you will be able to just type \"\$mkdir some/new\ +> /folder\"\r\ +> \n# and all the folders that need to be created will be\r\ +> \n# if the folder already existed, it is ignored\r\ +> \n\r\ +> \n:log info \"Creating \\\$mkdir function...\"\r\ +> \n\r\ +> \n:global mkdir do={\r\ +> \n # \$1 refers to the input variable the user passed in to us\r\ +> \n :local newFolder \$1;\r\ +> \n\r\ +> \n # we'll use this variable to report error messages that may arise\r\ +> \n :local errorMsg;\r\ +> \n\r\ +> \n # if the user put a slash on the end of the new folder name, remove \ +> it\r\ +> \n # i.e. \"some/test/\" becomes \"some/test\"\r\ +> \n :while (([:pick \$newFolder ([:len \$newFolder] - 1)]) = \"/\") do={\ +> \r\ +> \n :set newFolder [:pick \$newFolder 0 ([:len \$newFolder] - 1)]\r\ +> \n }\r\ +> \n\r\ +> \n # if the user put a slash at the beginning of the new folder name, r\ +> emove it\r\ +> \n # i.e. \"/some/test\" becomes \"some/test\"\r\ +> \n :while ([:pick \$newFolder 0] = \"/\") do={\r\ +> \n :set newFolder [:pick \$newFolder 1 [:len \$newFolder]]\r\ +> \n }\r\ +> \n\r\ +> \n :if ([/file find name=\$newFolder] != \"\") do={\r\ +> \n :set errorMsg \"'\$newFolder' already exists.\";\r\ +> \n :log debug \"mkdir: \$errorMsg\";\r\ +> \n :return \$newFolder;\r\ +> \n }\r\ +> \n\r\ +> \n # the name of the temp file to create\r\ +> \n :local tempfile \"mkdir_temp_file.txt\";\r\ +> \n\r\ +> \n # port 0 is unlikely to be open to HTTP requests...\r\ +> \n :local fakeURL \"http://127.0.0.1:0/should-not-exist.txt\";\r\ +> \n\r\ +> \n :local fullTempPath (\$newFolder . \"/\" . \$tempfile);\r\ +> \n\r\ +> \n # this is where the folder creation happens\r\ +> \n # `as-value` prevents status messages from printing\r\ +> \n # wrapping in a `:do {...} on-error` prevents error messages from pr\ +> inting.\r\ +> \n :do {\r\ +> \n /tool fetch dst-path=\"\$fullTempPath\" url=\"\$fakeURL\" durati\ +> on=0.001s as-value;\r\ +> \n } on-error={}\r\ +> \n\r\ +> \n # this waits for the temporary dir to show up so there isn't a race \ +> \r\ +> \n # condition where you call mkdir and then try to do something before\ +> \_the \r\ +> \n # folder is actually created\r\ +> \n :local count 0;\r\ +> \n :while ([/file find name=\"\$newFolder\"] = \"\") do={\r\ +> \n :if (\$count >= 50) do={\r\ +> \n # after 50 * 0.1s delays, it's been 5 seconds, which should \ +> be long enough\r\ +> \n :set errorMsg \"New folder could not be created at \$newFold\ +> er\";\r\ +> \n :log error \"mkdir: \$errorMsg\"\r\ +> \n :error \$errorMsg\r\ +> \n }\r\ +> \n :delay 0.1s;\r\ +> \n :set count (\$count + 1);\r\ +> \n }\r\ +> \n\r\ +> \n :return \$newFolder;\r\ +> \n}\r\ +> \n\r\ +> \n:log info \"Created function \\\$mkdir\"\r\ +> \n" +> add comment="Certificates CloudFlare, Quad9 " dont-require-permissions=no \ +> name=CustomUpdate-Certificates owner=vados policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ +> \r\ +> \n/tool fetch url=https://curl.se/ca/cacert.pem;\r\ +> \n/certificate import file-name=cacert.pem passphrase=\"\";\r\ +> \n\r\ +> \n# CloudFlare\r\ +> \n#/tool/fetch mode=https url=https://developers.cloudflare.com/ssl/static\ +> /origin_ca_ecc_root.pem;\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\r\ +> \n\r\ +> \n# test\r\ +> \n:global FetchUserAgentStr;\r\ +> \n:global CleanName;\r\ +> \n\r\ +> \n\r\ +> \n:local CommonName \"DigiCert Global G2 TLS RSA SHA256 2020 CA1\";\r\ +> \n:local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ +> \n:local FetchUrl \"https://cacerts.digicert.com/\";\r\ +> \n#\"https://mkcert.org/generate/\";\r\ +> \n:local FetchName [ :serialize to=json ({ \$CommonName }) ];\r\ +> \n\r\ +> \n\r\ +> \n:log warning (\"/tool/fetch check-certificate=yes-without-crl mode=https\ +> \_http-header-field=\" . ({ [ \$FetchUserAgentStr \$0 ] }) . \$FetchUrl . \ +> \" http-data=\" . \$FetchName . \" dst-path=\" . \$FileName . \" as-value\ +> \");\r\ +> \n/certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ +> \n\r\ +> \n#:global AmScriptsUrl;\r\ +> \n#google\r\ +> \n#:local CertName \"GTS-Root-R1.pem\";\r\ +> \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ +> \r\ +> \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ +> \n#:set CertName \"GTS-Root-R4.pem\";\r\ +> \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ +> \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#:set CertName \"ISRG-Root-X1.pem\";\r\ +> \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ +> \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#:set CertName \"Starfield-Root-Certificate-Authority-G2.pem\";\r\ +> \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ +> \r\ +> \n#/certificate/import file-name=\$CertName passphrase=\"\";\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=script1 owner=vados policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ +> tool/netwatch/add comment=\"doh-fallback, doh-url=https://security.cloudfl\ +> are-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Clou\ +> dFlare-doh IPv4 1\" host=1.1.1.1;\r\ +> \n/tool/netwatch/add comment=\"doh, doh-url=https://security.cloudflare-dn\ +> s.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=CloudFlare\ +> -doh IPv4 2\" host=1.0.0.1;\r\ +> \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ +> /dns.google/dns-query, doh-cert=DigiCert Global Root G2, notify, name=Goog\ +> le-doh IPv4 1\" host=8.8.8.8;\r\ +> \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ +> /dns.google/dns-query, doh-cert=DigiCert Global Root G2, notify, name=Goog\ +> le-doh IPv4 2\" host=8.8.4.4;\r\ +> \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ +> /dns.quad9.net/dns-query, doh-cert=DigiCert Global Root G3, notify, name=C\ +> uad9-doh IPv4 1\" host=9.9.9.9;\r\ +> \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ +> /dns.quad9.net/dns-query, doh-cert=DigiCert Global Root G3, notify, name=C\ +> uad9-doh IPv4 2\" host=149.112.112.112;\r\ +> \n\r\ +> \n/tool/netwatch/add comment=\"dns, notify, name=ns1.unet.ws\" host=91.192\ +> .189.2;" +> add dont-require-permissions=no name=script2 owner=vados policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ +> \r\ +> \n\r\ +> \n:global InspectVar;\r\ +> \n#:global RouterBoard;\r\ +> \n#:do {:set RouterBoard [[ :parse \"/system/routerboard/get\" ]]} on-erro\ +> r={}\r\ +> \n#:global Resources;\r\ +> \n#:do {:set Resources [[ :parse \"/system/resource/get\" ]]} on-error={}\ +> \r\ +> \n\r\ +> \n#:global Updates;\r\ +> \n#:do {:set Updates [[ :parse \"/system/package/update/get\" ]]} on-error\ +> ={}\r\ +> \n\r\ +> \n#:global PkgUpdStatus \"undefined\";\r\ +> \n#:local Update [/system/package/update/get];\r\ +> \n#:retry command={:set PkgUpdStatus (\$Update->\"status\")} delay=200ms m\ +> ax=5 on-error={};\r\ +> \n\r\ +> \n#:log warning [\$InspectVar \$RouterBoard];\r\ +> \n#:put [\$InspectVar \$RouterBoard];\r\ +> \n\r\ +> \n#:retry command={/system/package/update check-for-updates without-paging\ +> \_as-value} delay=300ms max=5 on-error={};\r\ +> \n\r\ +> \n#:global arrDeviceInfo ({});\r\ +> \n#:local arrDeviceInfo ({\"section\":{\"key\":\"\"; \"value\":\"\";};});\ +> \r\ +> \n#:set (\$arrDeviceInfo->\"RouterBoard\") [[ :parse \"/system/routerboard\ +> /get\" ]];\r\ +> \n#:retry command={:set (\$arrDeviceInfo->\"Updates\") [[ :parse \"/system\ +> /package/update/get\" ]]} delay=500ms max=5 on-error={:log error (\"Error\ +> \")};\r\ +> \n# [:toarray {\"section\"={\"key\"=\"\"; \"value\"=\"\";} }];\r\ +> \n# [:toarray \"\"];\r\ +> \n:local arrDeviceInfo ({});\r\ +> \n\r\ +> \n:set (\$arrDeviceInfo->\"devUpdates\") {\"channel\"; \"stable\";};\r\ +> \n#:do {:set (arrDeviceInfo->\"Updates\") [[ :parse \"/system/package/upda\ +> te/get\" ]]} on-error={:log error (\$error)}\r\ +> \n\$InspectVar \$arrDeviceInfo;\r\ +> \n" +347,348c752 +< add comment="Global Config Script" dont-require-permissions=no name=\ +< AM-GlobalConfig owner=AM-GlobalConfig policy=\ +--- +> add dont-require-permissions=no name=fetchCustom owner=vados policy=\ +350,483c754,755 +< !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 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 \"\";\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/\");\r\ +< \n:global CertRenewTime 2w;\r\ +< \n:global CertRenewPass {\r\ +< \n \"\";\r\ +< \n# \"v3ry-s3cr3t\";\r\ +< \n# \"4n0th3r-s3cr3t\";\r\ +< \n};\r\ +< \n:global CertWarnTime 1w;\r\ +< \n:global CertIssuedExportPass {\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\ +--- +> \r\ +> \n#root.mnuc.backuper\r\ +485,486c757,764 +< \n# Certificate Password\r\ +< \n:global BackupCertsPasswd \"\";\r\ +--- +> \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ +> 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ +> \n#:local Script AM-GlobalConfig;\r\ +> \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ +> heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ +> sc\") output=user as-value ]->\"data\");\r\ +> \n#/usb1/mikro_bak_rsa.pub\r\ +> \n#:local FetchFile AM-DOHCert.rsc\r\ +488,489c766,767 +< \n# User-Manager Export\r\ +< \n:global BackupUserMan false;\r\ +--- +> \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ +> \n:global MkDir;\r\ +491,492c769,770 +< \n# The Dude Export\r\ +< \n:global BackupDude false;\r\ +--- +> \n:local FetchFile Binary.backup\r\ +> \n:local localDir \"[usb1]/scripts/\";\r\ +494,526c772 +< \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\$MkDir \$localDir;\r\ +528,530c774,775 +< \n# signal we are ready\r\ +< \n:set GlobalConfReady true;\r\ +< \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ +--- +> \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ +> \$localDir . \$FetchFile);\r\ +557c802 +< \n:global CommitId \"main/368/b69ddb1c\";\r\ +--- +> \n:global CommitId \"main/366/ab812be1\";\r\ +560c805 +< r: root, in 09:35:02 01.05.2026.\";\r\ +--- +> r: root, in 08:50:03 01.05.2026.\";\r\ +1159,1160d1403 +< \n# Usage: \$CertificateAvailable \"Common Name\" \"intended use for certi\ +< ficate (e.g. \"fetch\", \"tls\", \"all\", \"undefined\")\"\r\ +1165a1409 +> \n :global LogPrint;\r\ +1169c1413 +< \n :log warning (\$0 . \"The intended use is undefined!\");\r\ +--- +> \n \$LogPrint warning \$0 (\"The intended use is undefined!\");\r\ +1171a1416 +> \n\r\ +1175,1176c1420,1421 +< \n :log warning (\$0 . \"This system has low free flash space but \" . \ +< \\\r\ +--- +> \n \$LogPrint warning \$0 (\"This system has low free flash space but \ +> \" . \\\r\ +1178a1424 +> \n\r\ +1180c1426 +< \n :log warning (\$0 . \"No CommonName given!\");\r\ +--- +> \n \$LogPrint warning \$0 (\"No CommonName given!\");\r\ +1182a1429 +> \n\r\ +1191a1439 +> \n\r\ +1194,1195c1442,1443 +< \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ +< \"' not available.\");\r\ +--- +> \n \$LogPrint info \$0 (\"Certificate with CommonName '\" . \$CommonNam\ +> e . \"' not available.\");\r\ +1199a1448 +> \n\r\ +1202,1203c1451,1452 +< \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ +< \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ +--- +> \n \$LogPrint info \$0 (\"There are \" . \$CertCount . \" Certificates \ +> with CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ +1205a1455 +> \n\r\ +1214,1215c1464,1465 +< \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ +< is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ +--- +> \n \$LogPrint info \$0 (\"Certificate chain for '\" . \$CommonName . \ +> \"' is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ +1227d1476 +< \n# Usage: \$CertificateDownload \"Common Name\"\r\ +1229a1479 +> \n\r\ +1231a1482 +> \n\r\ +1234a1486 +> \n :global LogPrint;\r\ +1238,1239c1490,1491 +< \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ +< \r\ +--- +> \n \$LogPrint info \$0 (\"Downloading and importing certificate with \" .\ +> \_\\\r\ +1250,1251c1502,1503 +< \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ +< e '\" . \$CommonName . \\\r\ +--- +> \n \$LogPrint warning \$0 (\"Failed downloading certificate with Common\ +> Name '\" . \$CommonName . \\\r\ +1265,1266c1517,1518 +< \n :log error (\$0 . \" Required certificate is not available.\");\ +< \r\ +--- +> \n \$LogPrint error \$0 (\"Required certificate is not available.\"\ +> );\r\ +1280,1281c1532,1533 +< \n :log warning (\$0 . \" Failed downloading certificate with CommonN\ +< ame '\" . \$CommonName . \"'!\");\r\ +--- +> \n \$LogPrint warning \$0 (\"Failed downloading certificate with Comm\ +> onName '\" . \$CommonName . \"'!\");\r\ +1284a1537 +> \n\r\ +1287a1541 +> \n\r\ +1292,1293c1546,1547 +< \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ +< e . \"' still unavailable!\");\r\ +--- +> \n \$LogPrint warning \$0 (\"Certificate with CommonName '\" . \$Common\ +> Name . \"' still unavailable!\");\r\ +1295a1550 +> \n\r\ +1304d1558 +< \n# Usage: \$CertificateNameByCN \"Common Name\"\r\ +1306a1561 +> \n\r\ +1307a1563,1564 +> \n :global LogPrint;\r\ +> \n\r\ +1311c1568 +< \n :log warning (\$0 .\" No matching certificate found.\");\r\ +--- +> \n \$LogPrint warning \$0 (\"No matching certificate found.\");\r\ +2478,2479c2735,2736 +< add comment="Example add WU servers to AddrList" dont-require-permissions=no \ +< name=WU-add-to-list owner=vados policy=\ +--- +> add comment="Import ssh keys for public key authentication" \ +> dont-require-permissions=no name=AM-SshKeysImport owner=vados policy=\ +2481,2504c2738,2843 +< \_Script to add target web sites DNS IP addresses into address list\r\ +< \n# Tested with Mikrotik 6.4x\r\ +< \n# Syed Jahanzaib / aacable@hotmail.com\r\ +< \n# List name\r\ +< \n:local LISTNAME \"windowsupdateslist\"\r\ +< \n# Web site names which will be added in address list\r\ +< \n:local TARGET1 \"update.microsoft.com\"\r\ +< \n:local TARGET2 \"download.windowsupdate.com\"\r\ +< \n:local TARGET3 \"download.microsoft.com\"\r\ +< \n:local TARGET4 \"windowsupdate.com\"\r\ +< \n:local TARGET5 \"ntservicepack.microsoft.com\"\r\ +< \n:local TARGET6 \"wustat.windows.com\"\r\ +< \n:local TARGET7 \"stats.microsoft.com\"\r\ +< \n:local TARGET8 \"mp.microsoft.com\"\r\ +< \n:local TARGET9 \"ws.microsoft.com\"\r\ +< \n:local TARGET10 \"sls.update.microsoft.com.akadns.net\"\r\ +< \n:local TARGET11 \"vortex.data.microsoft.com\"\r\ +< \n:local TARGET12 \"vortex-win.data.microsoft.com\"\r\ +< \n:local TARGET13 \"fe2.update.microsoft.com.akadns.net\"\r\ +< \n:local TARGET14 \"statsfe2.update.microsoft.com.akadns.net\"\r\ +< \n \r\ +< \n# 1st time runner, check if address list is not created previously and a\ +< dd entries in it for 1st time usage\r\ +< \n:if ( [/ip firewall address-list find where list=\$LISTNAME] = \"\") do=\ +--- +> !rsc by Vados\r\ +> \n# RouterOS script: AM-SshKeysImport\r\ +> \n# Script comment: Import ssh keys for public key authentication\r\ +> \n# Copyright (c) 2005-2026 Vados \r\ +> \n#\r\ +> \n# requires RouterOS, version=7.19\r\ +> \n#\r\ +> \n:local ExitOK false;\r\ +> \n:onerror Err {\r\ +> \n :global GlobalConfReady; :global GlobalFuncReady;\r\ +> \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ +> \\\r\ +> \n do={ :error (\"Global configs or functions not ready.\"); }; } del\ +> ay=500ms max=50;\r\ +> \n :local ScriptName [ :jobname ];\r\ +> \n\r\ +> \n:global SSHKeysImport;\r\ +> \n:global SSHKeysImportFile;\r\ +> \n\r\ +> \n# import single key passed as string\r\ +> \n:set SSHKeysImport do={ :onerror Err {\r\ +> \n :local Key [ :tostr \$1 ];\r\ +> \n :local User [ :tostr \$2 ];\r\ +> \n :global GetRandom20CharAlNum;\r\ +> \n :global LogPrint;\r\ +> \n :global MkDir;\r\ +> \n :global RmDir;\r\ +> \n :global WaitForFile;\r\ +> \n :if ([ :len \$Key ] = 0 || [ :len \$User ] = 0) do={\r\ +> \n \$LogPrint warning \$0 (\"Missing argument(s), please pass key and u\ +> ser!\");\r\ +> \n :return false;\r\ +> \n }\r\ +> \n :if ([ :len [ /user/find where name=\$User ] ] = 0) do={\r\ +> \n \$LogPrint warning \$0 (\"User '\" . \$User . \"' does not exist.\")\ +> ;\r\ +> \n :return false;\r\ +> \n }\r\ +> \n :local KeyVal ([ :deserialize \$Key delimiter=\" \" from=dsv options=d\ +> sv.plain ]->0);\r\ +> \n :if (!(\$KeyVal->0 = \"ssh-ed25519\" || \$KeyVal->0 = \"ssh-rsa\")) do\ +> ={\r\ +> \n \$LogPrint warning \$0 (\"SSH key of type '\" . \$KeyVal->0 . \"' is\ +> \_not supported.\");\r\ +> \n :return false;\r\ +> \n }\r\ +> \n :local FingerPrintMD5 [ :convert from=base64 transform=md5 to=hex (\$K\ +> eyVal->1) ];\r\ +> \n :local RegEx (\"\\\\bmd5=\" . \$FingerPrintMD5 . \"\\\\b\");\r\ +> \n :if ([ :len [ /user/ssh-keys/find where user=\$User \\\r\ +> \n (key-owner~\$RegEx or info~\$RegEx) ] ] > 0) do={\r\ +> \n \$LogPrint warning \$0 (\"The ssh public key (MD5:\" . \$FingerPrint\ +> MD5 . \\\r\ +> \n \") is already available for user '\" . \$User . \"'.\");\r\ +> \n :return false;\r\ +> \n }\r\ +> \n :if ([ \$MkDir \"tmpfs/ssh-keys-import\" ] = false) do={\r\ +> \n \$LogPrint warning \$0 (\"Creating directory 'tmpfs/ssh-keys-import'\ +> \_failed!\");\r\ +> \n :return false;\r\ +> \n }\r\ +> \n :local FileName (\"tmpfs/ssh-keys-import/key-\" . [ \$GetRandom20CharA\ +> lNum 6 ] . \".pub\");\r\ +> \n /file/add name=\$FileName contents=(\$Key . \", md5=\" . \$FingerPrint\ +> MD5);\r\ +> \n \$WaitForFile \$FileName;\r\ +> \n :onerror Err {\r\ +> \n /user/ssh-keys/import public-key-file=\$FileName user=\$User;\r\ +> \n \$LogPrint info \$0 (\"Imported ssh public key (\" . \$KeyVal->2 . \ +> \", \" . \$KeyVal->0 . \", \" . \\\r\ +> \n \"MD5:\" . \$FingerPrintMD5 . \") for user '\" . \$User . \"'.\");\ +> \r\ +> \n \$RmDir \"tmpfs/ssh-keys-import\";\r\ +> \n } do={\r\ +> \n \$LogPrint warning \$0 (\"Failed importing key: \" . \$Err);\r\ +> \n \$RmDir \"tmpfs/ssh-keys-import\";\r\ +> \n :return false;\r\ +> \n }\r\ +> \n} do={\r\ +> \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ +> \n} }\r\ +> \n\r\ +> \n# import keys from a file\r\ +> \n:set SSHKeysImportFile do={ :onerror Err {\r\ +> \n :local FileName [ :tostr \$1 ];\r\ +> \n :local User [ :tostr \$2 ];\r\ +> \n :global EitherOr;\r\ +> \n :global FileExists;\r\ +> \n :global LogPrint;\r\ +> \n :global ParseKeyValueStore;\r\ +> \n :global SSHKeysImport;\r\ +> \n :if ([ :len \$FileName ] = 0 || [ :len \$User ] = 0) do={\r\ +> \n \$LogPrint warning \$0 (\"Missing argument(s), please pass file name\ +> \_and user!\");\r\ +> \n :return false;\r\ +> \n }\r\ +> \n :if ([ \$FileExists \$FileName ] = false) do={\r\ +> \n \$LogPrint warning \$0 (\"File '\" . \$FileName . \"' does not exist\ +> .\");\r\ +> \n :return false;\r\ +> \n }\r\ +> \n :local Keys [ :tolf [ /file/get \$FileName contents ] ];\r\ +> \n :foreach KeyVal in=[ :deserialize \$Keys delimiter=\" \" from=dsv opti\ +> ons=dsv.plain ] do={\r\ +> \n :local Continue false;\r\ +> \n :if (\$KeyVal->0 = \"ssh-ed25519\" || \$KeyVal->0 = \"ssh-rsa\") do=\ +2506,2509c2845,2880 +< \n:log warning \"No address list for \$TARGET1 found ! creating and adding\ +< \_resolved entry for 1st time usage ... zaib\"\r\ +< \n \r\ +< \n:local FirstAddr [resolve \$TARGET1];\r\ +--- +> \n :if ([ \$SSHKeysImport (\$KeyVal->0 . \" \" . \$KeyVal->1 . \" \" \ +> . \$KeyVal->2) \$User ] = false) do={\r\ +> \n \$LogPrint warning \$0 (\"Failed importing key for user '\" . \$\ +> User . \"'.\");\r\ +> \n }\r\ +> \n :set Continue true;\r\ +> \n }\r\ +> \n :if (\$Continue = false && \$KeyVal->0 = \"#\") do={\r\ +> \n :set User [ \$EitherOr ([ \$ParseKeyValueStore (\$KeyVal->1) ]->\"\ +> user\") \$User ];\r\ +> \n :set Continue true;\r\ +> \n }\r\ +> \n :if (\$Continue = false && [ :len (\$KeyVal->0) ] > 0) do={\r\ +> \n \$LogPrint warning \$0 (\"SSH key of type '\" . \$KeyVal->0 . \"' \ +> is not supported.\");\r\ +> \n }\r\ +> \n }\r\ +> \n} do={\r\ +> \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ +> \n} }\r\ +> \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ +> \n" +> add comment="Enable or disable ip addresses based on bridge port state" \ +> dont-require-permissions=no name=mod/AM-IPbridge owner=vados policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ +> !rsc by Vados\r\ +> \n# RouterOS script: mod/AM-IPbridge\r\ +> \n# Script comment: Enable or disable ip addresses based on bridge port st\ +> ate\r\ +> \n# Copyright (c) 2005-2026 Vados \r\ +> \n#\r\ +> \n#\r\ +> \n# requires RouterOS, version=7.19\r\ +> \n# requires device-mode, fetch\r\ +> \n#\r\ +> \n:local ScriptName [ :jobname ];\r\ +2511,2530c2882,2883 +< \n/ip firewall address-list add address=\$FirstAddr list=\$LISTNAME commen\ +< t=\$TARGET1;\r\ +< \n} else={\r\ +< \n:log warning \"Previous List for \$LISTNAME found ! moving forward and c\ +< hecking if DNS entries can be added in it ...\"\r\ +< \n}\r\ +< \n \r\ +< \n# Check DNS entries and collect matching names\r\ +< \n:foreach i in=[/ip dns cache find name~\"(\$TARGET1|\$TARGET2|\$TARGET3|\ +< \$TARGET4|\$TARGET5|\$TARGET6|\$TARGET7|\$TARGET8|\$TARGET9|\$TARGET10|\$T\ +< ARGET11|\$TARGET12|\$TARGET13|\$TARGET14)\" && \$type ~ \"^(A|CNAME)\\\$\"\ +< \_] do={\r\ +< \n \r\ +< \n# Get IP Address from the names and hold it in temporary buffer\r\ +< \n:local Buffer [/ip dns cache get \$i data];\r\ +< \ndelay delay-time=10ms\r\ +< \n \r\ +< \n# Check if entry is already not exists, then OK, otherwise ignore duplic\ +< ation\r\ +< \n:if ( [/ip firewall address-list find where address=\$Buffer] = \"\") do\ +--- +> \n# Add this Script scheduler\r\ +> \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ +2532,2541c2885,2890 +< \n \r\ +< \n# Fetch DNS names for the entries\r\ +< \n:local sitednsname [/ip dns cache get \$i name] ;\r\ +< \n \r\ +< \n# Print name in LOG window\r\ +< \n:log info (\"added entry: \$sitednsname \$Buffer\");\r\ +< \n \r\ +< \n# Add IP addresses and there names to the address list\r\ +< \n/ip firewall address-list add address=\$Buffer list=\$LISTNAME comment=\ +< \$sitednsname;\r\ +--- +> \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ +> T SET! Fix It Now!\");\r\ +> \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ +> \n:local Comment \"Every Day Check For Fake Bridges\";\r\ +> \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ +> rtup interval=1d on-event=\$OnEvent;\r\ +2543,2545c2892,2907 +< \n}" +< add comment="Add Hosts DNS names to NoWG addres list" \ +< dont-require-permissions=no name=Add2vdsina owner=vados policy=\ +--- +> \n\r\ +> \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ +> \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ +> \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ +> 0) do={\r\ +> \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ +> inactive=no ] ] = 0) do={\r\ +> \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ +> \n } else={\r\ +> \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ +> \n }\r\ +> \n }\r\ +> \n}\r\ +> \n" +> add comment="Check for certificates validity" dont-require-permissions=no \ +> name=AM-CheckCerts owner=vados policy=\ +2547,2551c2909,2912 +< \_Script to add target web sites DNS IP addresses into address list\r\ +< \n# Tested with Mikrotik 6.4x\r\ +< \n# \r\ +< \n# List name\r\ +< \n:local LISTNAME \"NoWG\"\r\ +--- +> !rsc by Vados\r\ +> \n# RouterOS script: AM-CheckCerts\r\ +> \n# Script comment: Check for certificates validity\r\ +> \n# Copyright (c) 2005-2026 Vados \r\ +2553,2575c2914,2916 +< \n# Web site names which will be added in address list\r\ +< \n:local TARGET1 \"comp-lenta.ru\"\r\ +< \n:local TARGET2 \"ok.ru\"\r\ +< \n:local TARGET3 \"vk.com\"\r\ +< \n#:local TARGET4 \"smtp.mail.me.com\"\r\ +< \n#:local TARGET4 \"windowsupdate.com\"\r\ +< \n#:local TARGET5 \"ntservicepack.microsoft.com\"\r\ +< \n#:local TARGET6 \"wustat.windows.com\"\r\ +< \n#:local TARGET7 \"stats.microsoft.com\"\r\ +< \n#:local TARGET8 \"mp.microsoft.com\"\r\ +< \n#:local TARGET9 \"ws.microsoft.com\"\r\ +< \n#:local TARGET10 \"sls.update.microsoft.com.akadns.net\"\r\ +< \n#:local TARGET11 \"vortex.data.microsoft.com\"\r\ +< \n#:local TARGET12 \"vortex-win.data.microsoft.com\"\r\ +< \n#:local TARGET13 \"fe2.update.microsoft.com.akadns.net\"\r\ +< \n#:local TARGET14 \"statsfe2.update.microsoft.com.akadns.net\"\r\ +< \n \r\ +< \n# 1st time runner, check if address list is not created previously and a\ +< dd entries in it for 1st time usage\r\ +< \n:if ( [/ip firewall address-list find where list=\$LISTNAME] = \"\") do=\ +< {\r\ +< \n:log warning \"No address list for \$TARGET1 found ! creating and adding\ +< \_resolved entry for 1st time usage ... zaib\"\r\ +--- +> \n#\r\ +> \n# requires RouterOS, version=7.19\r\ +> \n# requires device-mode, fetch\r\ +2577c2918,2930 +< \n:local FirstAddr [resolve \$TARGET1];\r\ +--- +> \n:local ExitOK false;\r\ +> \n:onerror Err {\r\ +> \n :global GlobalConfReady; :global GlobalFuncReady;\r\ +> \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ +> \\\r\ +> \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ +> delay=500ms max=50;\r\ +> \n :local ScriptName [ :jobname ];\r\ +> \n\r\ +> \n :global CertRenewTime;\r\ +> \n :global CertRenewUrl;\r\ +> \n :global CertWarnTime;\r\ +> \n :global Identity;\r\ +2579,2580c2932,2941 +< \n/ip firewall address-list add address=\$FirstAddr list=\$LISTNAME commen\ +< t=\$TARGET1;\r\ +--- +> \n :global CertificateAvailable;\r\ +> \n :global EscapeForRegEx;\r\ +> \n :global IfThenElse;\r\ +> \n :global LogPrint;\r\ +> \n :global ParseKeyValueStore;\r\ +> \n :global ScriptLock;\r\ +> \n :global SendNotification2;\r\ +> \n :global SymbolForNotification;\r\ +> \n :global UrlEncode;\r\ +> \n# :global WaitFullyConnected;\r\ +2582,2584c2943,2951 +< \n} else={\r\ +< \n:log warning \"Previous List for \$LISTNAME found ! moving forward and c\ +< hecking if DNS entries can be added in it ...\"\r\ +--- +> \n# Add this Script scheduler\r\ +> \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ +> ={\r\ +> \n:log warning (\"System Scheduler for script: \" . \$ScriptName . \" is N\ +> OT SET! Fix It Now!\");\r\ +> \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ +> \n:local Comment \"Every Day Check For Renew Certificates\";\r\ +> \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ +> rtup interval=1d on-event=\$OnEvent;\r\ +2587,2589d2953 +< \n# Check DNS entries and collect matching names\r\ +< \n:foreach i in=[/ip dns cache find name~\"(\$TARGET1|\$TARGET2|\$TARGET3|\ +< \$TARGET4)\" && \$type ~ \"^(A|CNAME)\\\$\" ] do={\r\ +2591c2955,2958 +< \n:log info (\"start entry: \$c \$sitednsname \$Buffer\");\r\ +--- +> \n :local CheckCertificatesDownloadImport do={\r\ +> \n :local ScriptName [ :tostr \$1 ];\r\ +> \n :local CertName [ :tostr \$2 ];\r\ +> \n :local FetchName [ :tostr \$3 ];\r\ +2593,2596c2960,2961 +< \n# Get IP Address from the names and hold it in temporary buffer\r\ +< \n:local Buffer [/ip dns cache get \$i data];\r\ +< \ndelay delay-time=10ms\r\ +< \n:log info (\"1: \$Buffer\"); \r\ +--- +> \n :global CertRenewUrl;\r\ +> \n :global CertRenewPass;\r\ +2598,2601c2963,2969 +< \n# Check if entry is already not exists, then OK, otherwise ignore duplic\ +< ation\r\ +< \n:if ( [/ip firewall address-list find where address=\$Buffer] = \"\") do\ +< ={\r\ +--- +> \n :global CertificateNameByCN;\r\ +> \n :global EscapeForRegEx;\r\ +> \n :global FetchUserAgentStr;\r\ +> \n :global LogPrint;\r\ +> \n :global RmFile;\r\ +> \n :global UrlEncode;\r\ +> \n :global WaitForFile;\r\ +2603,2629c2971,2976 +< \n:log info (\"2: \$Buffer\"); \r\ +< \n \r\ +< \n# Fetch DNS names for the entries\r\ +< \n:local sitednsname [/ip dns cache get \$i name] ;\r\ +< \n \r\ +< \n# Print name in LOG window\r\ +< \n:log info (\"added entry: \$i \$sitednsname \$Buffer\");\r\ +< \n \r\ +< \n# Add IP addresses and there names to the address list\r\ +< \n/ip firewall address-list add address=\$Buffer list=\$LISTNAME comment=\ +< \$sitednsname;\r\ +< \n}\r\ +< \n}" +< add comment="Flush DNS Cache" dont-require-permissions=no name=\ +< DNS_Flush_Cache owner=vados policy=\ +< ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=\ +< "/ip dns cache flush" +< add dont-require-permissions=no name=create_mkdir_function owner=vados \ +< policy=read,write source="# Written by Phillip Stromberg in 2019\r\ +< \n# Distributed under the MIT license\r\ +< \n# \r\ +< \n# \r\ +< \n# this creates a function called \$mkdir\r\ +< \n# from the command line you will be able to just type \"\$mkdir some/new\ +< /folder\"\r\ +< \n# and all the folders that need to be created will be\r\ +< \n# if the folder already existed, it is ignored\r\ +--- +> \n :foreach Type in={ \"p12\"; \"pem\" } do={\r\ +> \n :local CertFileName ([ \$UrlEncode \$FetchName ] . \".\" . \$Type)\ +> ;\r\ +> \n \$LogPrint debug \$ScriptName (\"Trying type '\" . \$Type . \"' fo\ +> r '\" . \$CertName . \\\r\ +> \n \"' (file '\" . \$CertFileName . \"')...\");\r\ +2631c2978,2992 +< \n:log info \"Creating \\\$mkdir function...\"\r\ +--- +> \n :do {\r\ +> \n :onerror Err {\r\ +> \n /tool/fetch check-certificate=yes-without-crl \\\r\ +> \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] \ +> }) \\\r\ +> \n (\$CertRenewUrl . \$CertFileName) dst-path=\$CertFileName \ +> as-value;\r\ +> \n } do={\r\ +> \n :if (!(\$Err ~ \"[Ss]tatus 404\")) do={\r\ +> \n \$LogPrint warning \$0 (\"Failed fetching certificate: \" . \ +> \$Err);\r\ +> \n }\r\ +> \n :error false;\r\ +> \n }\r\ +> \n \$WaitForFile \$CertFileName; \r\ +2633,2635c2994,3007 +< \n:global mkdir do={\r\ +< \n # \$1 refers to the input variable the user passed in to us\r\ +< \n :local newFolder \$1;\r\ +--- +> \n :local DecryptionFailed true;\r\ +> \n :foreach I,PassPhrase in=\$CertRenewPass do={\r\ +> \n :do {\r\ +> \n \$LogPrint debug \$ScriptName (\"Trying \" . \$I . \". passp\ +> hrase... \");\r\ +> \n :local Result [ /certificate/import file-name=\$CertFileName\ +> \_passphrase=\$PassPhrase as-value ];\r\ +> \n :if (\$Result->\"decryption-failures\" = 0) do={\r\ +> \n \$LogPrint debug \$ScriptName (\"Success!\");\r\ +> \n :set DecryptionFailed false;\r\ +> \n }\r\ +> \n } on-error={ }\r\ +> \n }\r\ +> \n \$RmFile \$CertFileName;\r\ +2637,2638c3009,3012 +< \n # we'll use this variable to report error messages that may arise\r\ +< \n :local errorMsg;\r\ +--- +> \n :if (\$DecryptionFailed = true) do={\r\ +> \n \$LogPrint warning \$ScriptName (\"Decryption failed for certi\ +> ficate file '\" . \$CertFileName . \"'.\");\r\ +> \n }\r\ +2640,2645c3014,3029 +< \n # if the user put a slash on the end of the new folder name, remove \ +< it\r\ +< \n # i.e. \"some/test/\" becomes \"some/test\"\r\ +< \n :while (([:pick \$newFolder ([:len \$newFolder] - 1)]) = \"/\") do={\ +< \r\ +< \n :set newFolder [:pick \$newFolder 0 ([:len \$newFolder] - 1)]\r\ +--- +> \n :foreach CertInChain in=[ /certificate/find where common-name!=\ +> \$CertName !private-key \\\r\ +> \n name~(\"^\" . [ \$EscapeForRegEx \$CertFileName ] . \"_[0-9]\ +> +\\\$\") \\\r\ +> \n !(subject-alt-name~(\"(^|\\\\W)(DNS|IP):\" . [ \$EscapeForRe\ +> gEx \$CertName ] . \"(\\\\W|\\\$)\")) \\\r\ +> \n !(common-name=[]) ] do={\r\ +> \n \$CertificateNameByCN [ /certificate/get \$CertInChain common-\ +> name ];\r\ +> \n }\r\ +> \n\r\ +> \n :return true;\r\ +> \n } on-error={\r\ +> \n \$LogPrint debug \$ScriptName (\"Could not download certificate \ +> file '\" . \$CertFileName . \"'.\");\r\ +> \n }\r\ +2648,2652c3032,3045 +< \n # if the user put a slash at the beginning of the new folder name, r\ +< emove it\r\ +< \n # i.e. \"/some/test\" becomes \"some/test\"\r\ +< \n :while ([:pick \$newFolder 0] = \"/\") do={\r\ +< \n :set newFolder [:pick \$newFolder 1 [:len \$newFolder]]\r\ +--- +> \n :return false;\r\ +> \n }\r\ +> \n\r\ +> \n :local FormatInfo do={\r\ +> \n :local Cert \$1;\r\ +> \n\r\ +> \n :global FormatLine;\r\ +> \n :global FormatMultiLines;\r\ +> \n :global IfThenElse;\r\ +> \n\r\ +> \n :local FormatExpire do={\r\ +> \n :global CharacterReplace;\r\ +> \n :return [ \$CharacterReplace [ \$CharacterReplace [ :tostr \$1 ] \ +> \"w\" \"w \" ] \"d\" \"d \" ];\r\ +2655,2658c3048,3079 +< \n :if ([/file find name=\$newFolder] != \"\") do={\r\ +< \n :set errorMsg \"'\$newFolder' already exists.\";\r\ +< \n :log debug \"mkdir: \$errorMsg\";\r\ +< \n :return \$newFolder;\r\ +--- +> \n :local FormatCertChain do={\r\ +> \n :local Cert \$1;\r\ +> \n\r\ +> \n :global ParseKeyValueStore;\r\ +> \n\r\ +> \n :local CertVal [ /certificate/get \$Cert ];\r\ +> \n\r\ +> \n :if ([ :typeof (\$CertVal->\"issuer\") ] = \"nothing\") do={\r\ +> \n :return \"self-signed\";\r\ +> \n }\r\ +> \n\r\ +> \n :local Return \"\";\r\ +> \n :for I from=0 to=5 do={\r\ +> \n :set Return (\$Return . [ \$ParseKeyValueStore (\$CertVal->\"iss\ +> uer\") ]->\"CN\");\r\ +> \n :if ([ :len [ /certificate/builtin/find where skid=(\$CertVal->\ +> \"akid\") ] ] > 0) do={\r\ +> \n :return \$Return;\r\ +> \n }\r\ +> \n :do {\r\ +> \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\ +> \"akid\") ] ];\r\ +> \n } on-error={\r\ +> \n :return (\$Return . \" (possibly incomplete!)\");\r\ +> \n }\r\ +> \n :if ((\$CertVal->\"akid\") = \"\" || (\$CertVal->\"akid\") = (\$\ +> CertVal->\"skid\")) do={\r\ +> \n :return \$Return;\r\ +> \n }\r\ +> \n :set Return (\$Return . \" -> \");\r\ +> \n }\r\ +> \n :return (\$Return . \"...\");\r\ +2661,2662c3082 +< \n # the name of the temp file to create\r\ +< \n :local tempfile \"mkdir_temp_file.txt\";\r\ +--- +> \n :local CertVal [ /certificate/get \$Cert ];\r\ +2664,2665c3084,3108 +< \n # port 0 is unlikely to be open to HTTP requests...\r\ +< \n :local fakeURL \"http://127.0.0.1:0/should-not-exist.txt\";\r\ +--- +> \n :return ( \\\r\ +> \n [ \$FormatLine \"Name\" (\$CertVal->\"name\") ] . \"\\n\" . \\\r\ +> \n [ \$IfThenElse ([ :len (\$CertVal->\"common-name\") ] > 0) ([ \$Fo\ +> rmatLine \"CommonName\" (\$CertVal->\"common-name\") ] . \"\\n\") ] . \\\r\ +> \n [ \$IfThenElse ([ :len (\$CertVal->\"subject-alt-name\") ] > 0) ([\ +> \_\$FormatMultiLines \"SubjectAltNames\" (\$CertVal->\"subject-alt-name\")\ +> \_] . \"\\n\") ] . \\\r\ +> \n [ \$FormatLine \"Private key\" [ \$IfThenElse ((\$CertVal->\"priva\ +> te-key\") = true) \"available\" \"missing\" ] ] . \"\\n\" . \\\r\ +> \n [ \$FormatLine \"Fingerprint\" (\$CertVal->\"fingerprint\") ] . \"\ +> \\n\" . \\\r\ +> \n [ \$IfThenElse ([ :len (\$CertVal->\"ca\") ] > 0) [ \$FormatLine \ +> \"Issuer\" (\$CertVal->\"ca\") ] [ \$FormatLine \"Issuer chain\" [ \$Forma\ +> tCertChain \$Cert ] ] ] . \"\\n\" . \\\r\ +> \n \"Validity:\\n\" . \\\r\ +> \n [ \$FormatLine \" days\" (\$CertVal->\"days-valid\") ] . \"\\n\ +> \" . \\\r\ +> \n [ \$FormatLine \" from\" (\$CertVal->\"invalid-before\") ] . \"\ +> \\n\" . \\\r\ +> \n [ \$FormatLine \" to\" (\$CertVal->\"invalid-after\") ] . \"\\n\ +> \" . \\\r\ +> \n [ \$FormatLine \" time left\" [ \$IfThenElse ((\$CertVal->\"exp\ +> ired\") = true) \"expired\" [ \$FormatExpire (\$CertVal->\"expires-after\"\ +> ) ] ] ]);\r\ +> \n }\r\ +2667c3110,3120 +< \n :local fullTempPath (\$newFolder . \"/\" . \$tempfile);\r\ +--- +> \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ +> \n :set ExitOK true;\r\ +> \n :error false;\r\ +> \n }\r\ +> \n# \$WaitFullyConnected;\r\ +> \n\r\ +> \n :foreach Cert in=[ /certificate/find where !revoked !ca !scep-url expi\ +> res-after<\$CertRenewTime ] do={\r\ +> \n :local CertVal [ /certificate/get \$Cert ];\r\ +> \n :local LastName;\r\ +> \n :local FetchName;\r\ +2669,2672d3121 +< \n # this is where the folder creation happens\r\ +< \n # `as-value` prevents status messages from printing\r\ +< \n # wrapping in a `:do {...} on-error` prevents error messages from pr\ +< inting.\r\ +2674,2676c3123,3128 +< \n /tool fetch dst-path=\"\$fullTempPath\" url=\"\$fakeURL\" durati\ +< on=0.001s as-value;\r\ +< \n } on-error={}\r\ +--- +> \n :if ([ :len \$CertRenewUrl ] = 0) do={\r\ +> \n \$LogPrint info \$ScriptName (\"No CertRenewUrl given.\");\r\ +> \n :error false;\r\ +> \n }\r\ +> \n \$LogPrint info \$ScriptName (\"Attempting to renew certificate '\ +> \" . (\$CertVal->\"name\") . \"'.\");\r\ +2678,2691c3130,3150 +< \n # this waits for the temporary dir to show up so there isn't a race \ +< \r\ +< \n # condition where you call mkdir and then try to do something before\ +< \_the \r\ +< \n # folder is actually created\r\ +< \n :local count 0;\r\ +< \n :while ([/file find name=\"\$newFolder\"] = \"\") do={\r\ +< \n :if (\$count >= 50) do={\r\ +< \n # after 50 * 0.1s delays, it's been 5 seconds, which should \ +< be long enough\r\ +< \n :set errorMsg \"New folder could not be created at \$newFold\ +< er\";\r\ +< \n :log error \"mkdir: \$errorMsg\"\r\ +< \n :error \$errorMsg\r\ +--- +> \n :local ImportSuccess false;\r\ +> \n :if ([ :len (\$CertVal->\"common-name\") ] > 0) do={\r\ +> \n :set LastName (\$CertVal->\"common-name\");\r\ +> \n :set FetchName \$LastName;\r\ +> \n :set ImportSuccess [ \$CheckCertificatesDownloadImport \$ScriptN\ +> ame \$LastName \$FetchName ];\r\ +> \n }\r\ +> \n :foreach SAN in=(\$CertVal->\"subject-alt-name\") do={\r\ +> \n :if (\$ImportSuccess = false) do={\r\ +> \n :set LastName [ :pick \$SAN ([ :find \$SAN \":\" ] + 1) [ :len\ +> \_\$SAN ] ];\r\ +> \n :set FetchName \$LastName;\r\ +> \n :set ImportSuccess [ \$CheckCertificatesDownloadImport \$Scrip\ +> tName \$LastName \$FetchName ];\r\ +> \n :if (\$ImportSuccess = false && [ :pick \$LastName 0 2 ] = \"*\ +> .\") do={\r\ +> \n :set FetchName (\"star.\" . [ :pick \$LastName 2 [ :len \$La\ +> stName ] ]);\r\ +> \n :set ImportSuccess [ \$CheckCertificatesDownloadImport \$Scr\ +> iptName \$LastName \$FetchName ];\r\ +> \n }\r\ +2693,2694c3152,3222 +< \n :delay 0.1s;\r\ +< \n :set count (\$count + 1);\r\ +--- +> \n }\r\ +> \n :if (\$ImportSuccess = false) do={ :error false; }\r\ +> \n\r\ +> \n :if ([ :len (\$CertVal->\"fingerprint\") ] > 0 && \$CertVal->\"fin\ +> gerprint\" != [ /certificate/get \$Cert fingerprint ]) do={\r\ +> \n \$LogPrint debug \$ScriptName (\"Certificate '\" . \$CertVal->\"\ +> name\" . \"' was updated in place.\");\r\ +> \n :set CertVal [ /certificate/get \$Cert ];\r\ +> \n } else={\r\ +> \n \$LogPrint debug \$ScriptName (\"Certificate '\" . \$CertVal->\"\ +> name\" . \"' was not updated, but replaced.\");\r\ +> \n\r\ +> \n :local CertNew [ /certificate/find where name~(\"^\" . [ \$Escap\ +> eForRegEx [ \$UrlEncode \$FetchName ] ] . \"\\\\.(p12|pem)_[0-9]+\\\$\") \ +> \\\r\ +> \n (common-name=(\$CertVal->\"common-name\") or subject-alt-name~\ +> (\"(^|\\\\W)(DNS|IP):\" . [ \$EscapeForRegEx \$LastName ] . \"(\\\\W|\\\$)\ +> \")) \\\r\ +> \n fingerprint!=[ :tostr (\$CertVal->\"fingerprint\") ] ];\r\ +> \n :local CertNewVal [ /certificate/get \$CertNew ];\r\ +> \n\r\ +> \n :if ((\$CertVal->\"expires-after\") > (\$CertNewVal->\"expires-a\ +> fter\")) do={\r\ +> \n /certificate/remove \$CertNew;\r\ +> \n \$LogPrint warning \$ScriptName (\"Old certificate is newer th\ +> an the new one. Aborting renew.\");\r\ +> \n :error false;\r\ +> \n }\r\ +> \n\r\ +> \n :if ((\$CertVal->\"private-key\") = true && (\$CertVal->\"privat\ +> e-key\") != (\$CertNewVal->\"private-key\")) do={\r\ +> \n /certificate/remove \$CertNew;\r\ +> \n \$LogPrint warning \$ScriptName (\"Old certificate '\" . (\$Ce\ +> rtVal->\"name\") . \"' has a private key, new certificate does not. Aborti\ +> ng renew.\");\r\ +> \n :error false;\r\ +> \n }\r\ +> \n\r\ +> \n :if ([ \$CertificateAvailable ([ \$ParseKeyValueStore (\$CertNew\ +> Val->\"issuer\") ]->\"CN\") \"fetch\" ] = false) do={\r\ +> \n \$LogPrint warning \$ScriptName (\"The certificate chain is no\ +> t available!\");\r\ +> \n }\r\ +> \n\r\ +> \n /ip/service/set certificate=(\$CertNewVal->\"name\") [ find wher\ +> e certificate=(\$CertVal->\"name\") ];\r\ +> \n\r\ +> \n /ip/ipsec/identity/set certificate=(\$CertNewVal->\"name\") [ fi\ +> nd where certificate=(\$CertVal->\"name\") ];\r\ +> \n /ip/ipsec/identity/set remote-certificate=(\$CertNewVal->\"name\ +> \") [ find where remote-certificate=(\$CertVal->\"name\") ];\r\ +> \n\r\ +> \n /ip/hotspot/profile/set ssl-certificate=(\$CertNewVal->\"name\")\ +> \_[ find where ssl-certificate=(\$CertVal->\"name\") ];\r\ +> \n\r\ +> \n /certificate/remove \$Cert;\r\ +> \n /certificate/set \$CertNew name=(\$CertVal->\"name\");\r\ +> \n :set Cert \$CertNew;\r\ +> \n :set CertVal [ /certificate/get \$CertNew ];\r\ +> \n }\r\ +> \n\r\ +> \n \$SendNotification2 ({ origin=\$ScriptName; silent=true; \\\r\ +> \n subject=([ \$SymbolForNotification \"lock-with-ink-pen\" ] . \"C\ +> ertificate renewed: \" . (\$CertVal->\"name\")); \\\r\ +> \n message=(\"A certificate on \" . \$Identity . \" has been renewe\ +> d.\\n\\n\" . [ \$FormatInfo \$Cert ]) });\r\ +> \n \$LogPrint info \$ScriptName (\"The certificate '\" . (\$CertVal->\ +> \"name\") . \"' has been renewed.\");\r\ +> \n } on-error={\r\ +> \n \$LogPrint debug \$ScriptName (\"Could not renew certificate '\" .\ +> \_(\$CertVal->\"name\") . \"'.\");\r\ +2695a3224 +> \n }\r\ +2697,2698c3226,3230 +< \n :return \$newFolder;\r\ +< \n}\r\ +--- +> \n :foreach Cert in=[ /certificate/find where !revoked !scep-url !(expire\ +> s-after=[]) \\\r\ +> \n expires-after<\$CertWarnTime !(fingerprint=[]) ] do\ +> ={\r\ +> \n :local CertVal [ /certificate/get \$Cert ];\r\ +2700c3232,3253 +< \n:log info \"Created function \\\$mkdir\"\r\ +--- +> \n :if ([ :len [ /certificate/scep-server/find where ca-cert=(\$CertVal\ +> ->\"ca\") ] ] > 0) do={\r\ +> \n \$LogPrint debug \$ScriptName (\"Certificate '\" . (\$CertVal->\"n\ +> ame\") . \"' is handled by SCEP, skipping.\");\r\ +> \n } else={\r\ +> \n :local State [ \$IfThenElse ((\$CertVal->\"expired\") = true) \"ex\ +> pired\" \"is about to expire\" ];\r\ +> \n\r\ +> \n \$SendNotification2 ({ origin=\$ScriptName; \\\r\ +> \n subject=([ \$SymbolForNotification \"lock-with-ink-pen,warning-s\ +> ign\" ] . \"Certificate warning: \" . (\$CertVal->\"name\")); \\\r\ +> \n message=(\"A certificate on \" . \$Identity . \" \" . \$State . \ +> \".\\n\\n\" . [ \$FormatInfo \$Cert ]) });\r\ +> \n \$LogPrint info \$ScriptName (\"The certificate '\" . (\$CertVal->\ +> \"name\") . \"' \" . \$State . \\\r\ +> \n \", it is invalid after \" . (\$CertVal->\"invalid-after\") . \ +> \".\");\r\ +> \n }\r\ +> \n }\r\ +> \n} do={\r\ +> \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ +> \n}\r\ +2702,2703c3255,3256 +< add comment="Certificates CloudFlare, Quad9 " dont-require-permissions=no \ +< name=Update-Certificates owner=vados policy=\ +--- +> add comment="Global Config Script" dont-require-permissions=no name=\ +> AM-GlobalConfig owner=AM-GlobalConfig policy=\ +2705,2708c3258,3296 +< \_CloudFlare\r\ +< \n/tool/fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobalRo\ +< otCA.crt.pem;\r\ +< \n/certificate/import file-name=DigiCertGlobalRootCA.crt.pem passphrase=\"\ +--- +> !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 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 \"\";\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\ +2710,2718c3298,3310 +< \n/tool/ fetch mode=https url=https://cacerts.digicert.com/DigiCertGlobalG\ +< 2TLSRSASHA2562020CA1-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/DigiCertGlobal\ +< G3TLSECCSHA3842020CA1-1.crt.pem\"\r\ +< \n/certificate/import file-name=DigiCertGlobalG3TLSECCSHA3842020CA1-1.crt.\ +< pem passphrase=\"\";\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\ +2720,2723c3312,3415 +< \n#:global AmScriptsUrl;\r\ +< \n#google\r\ +< \n#:local CertName \"GTS-Root-R1.pem\";\r\ +< \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ +--- +> \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/\");\r\ +> \n:global CertRenewTime 2w;\r\ +> \n:global CertRenewPass {\r\ +> \n \"\";\r\ +> \n# \"v3ry-s3cr3t\";\r\ +> \n# \"4n0th3r-s3cr3t\";\r\ +> \n};\r\ +> \n:global CertWarnTime 1w;\r\ +> \n:global CertIssuedExportPass {\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={\ +2725,2727c3417,3499 +< \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ +< \n#:set CertName \"GTS-Root-R4.pem\";\r\ +< \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ +--- +> \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="Renew locally issued certificates" dont-require-permissions=no \ +> name=AM-CertsRenew owner=AM-CertsRenew policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ +> !rsc by Vados\r\ +> \n# RouterOS script: AM-CertsRenew\r\ +> \n# Script comment: Renew locally issued certificates\r\ +> \n# Copyright (c) 2005-2026 Vados \r\ +> \n#\r\ +> \n#\r\ +> \n# requires RouterOS, version=7.19\r\ +> \n#\r\ +> \n:local ExitOK false;\r\ +> \n:onerror Err {\r\ +> \n :global GlobalConfReady; :global GlobalFuncReady;\r\ +> \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ +> \\\r\ +> \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ +> delay=500ms max=50;\r\ +> \n :local ScriptName [ :jobname ];\r\ +> \n\r\ +> \n :global CertIssuedExportPass;\r\ +> \n\r\ +> \n :global LogPrint;\r\ +> \n :global MkDir;\r\ +> \n :global ScriptLock;\r\ +> \n\r\ +> \n# Add this Script scheduler\r\ +> \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ +> ={\r\ +> \n:log warning (\"System Scheduler for script: \" . \$ScriptName . \" is N\ +> OT SET! Fix It Now!\");\r\ +> \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ +> \n:local Comment \"Every Day Check For Update Local Issued Certificates.\"\ +> ;\r\ +> \n#/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=st\ +> artup interval=1d on-event=\$OnEvent;\r\ +> \n}\r\ +> \n\r\ +> \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ +> \n :set ExitOK true;\r\ +> \n :error false;\r\ +> \n }\r\ +> \n\r\ +> \n :foreach Cert in=[ /certificate/find where issued expires-after<3w ] d\ +> o={\r\ +> \n :local CertVal [ /certificate/get \$Cert ];\r\ +> \n /certificate/issued-revoke \$Cert;\r\ +> \n /certificate/set name=(\$CertVal->\"name\" . \"-revoked-\" . [ /syst\ +> em/clock/get date ]) \$Cert;\r\ +> \n /certificate/add name=(\$CertVal->\"name\") common-name=(\$CertVal->\ +> \"common-name\") \\\r\ +> \n key-usage=(\$CertVal->\"key-usage\") subject-alt-name=(\$CertVal\ +> ->\"subject-alt-name\");\r\ +> \n /certificate/sign (\$CertVal->\"name\") ca=(\$CertVal->\"ca\");\r\ +> \n :if ([ :typeof (\$CertIssuedExportPass->(\$CertVal->\"common-name\")\ +> ) ] = \"str\") do={\r\ +> \n :if ([ \$MkDir \"cert-issued\" ] = true) do={\r\ +> \n /certificate/export-certificate (\$CertVal->\"name\") type=pkcs1\ +> 2 \\\r\ +> \n file-name=(\"cert-issued/\" . \$CertVal->\"common-name\") \\\ +2729,2731c3501,3553 +< \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);\ +--- +> \n export-passphrase=(\$CertIssuedExportPass->(\$CertVal->\"com\ +> mon-name\"));\r\ +> \n \$LogPrint info \$ScriptName (\"Issued a new certificate for '\"\ +> \_. \$CertVal->\"common-name\" . \\\r\ +> \n \"', exported to 'cert-issued/\" . \$CertVal->\"common-name\" \ +> . \".p12'.\");\r\ +> \n } else={\r\ +> \n \$LogPrint warning \$ScriptName (\"Failed creating directory, no\ +> t exporting certificate.\");\r\ +> \n }\r\ +> \n } else={\r\ +> \n \$LogPrint info \$ScriptName (\"Issued a new certificate for '\" .\ +> \_\$CertVal->\"common-name\" . \"'.\");\r\ +> \n }\r\ +> \n }\r\ +> \n} do={\r\ +> \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ +> \n}\r\ +> \n" +> add comment="Update and Backup send notification to EMail" \ +> dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ +> AM-Backup-UpdateEmail policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ +> !rsc by Vados\r\ +> \n# RouterOS script: AM-Backup-UpdateEmail\r\ +> \n# Script comment: Update and Backup send notification to EMail\r\ +> \n# Copyright (c) 2007-2026 Vados \r\ +> \n#\r\ +> \n#\r\ +> \n# requires RouterOS, version>=6.43.7\r\ +> \n# requires device-mode, fetch\r\ +> \n#\r\ +> \n# Updated: 15/04/2025\r\ +> \n# Website: https://github.com/beeyev\r\ +> \n# Notification e-mail (Make sure you have configured Email settings in T\ +> ools -> Email)\r\ +> \n:local ExitOK false;\r\ +> \n:onerror Err {\r\ +> \n :global GlobalConfReady; :global GlobalFuncReady;\r\ +> \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ +> \\\r\ +> \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ +> delay=500ms max=50;\r\ +> \n :local ScriptName [ :jobname ];\r\ +> \n\r\ +> \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ +> = true) do={:return \$2}; :return \$3;}\r\ +> \n:local EitherOr do={\r\ +> \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ +> n \$1} else={:return \$2};}\r\ +> \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ +> rn \$1} else={:return \$2};}\r\ +> \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ +2733,2736c3555,3583 +< \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ +< \n#internet\r\ +< \n#:set CertName \"ISRG-Root-X1.pem\";\r\ +< \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ +--- +> \n}\r\ +> \n:local ExitError do={\r\ +> \n :local ExitOK [ :tostr \$1 ];\r\ +> \n :local Name [ :tostr \$2 ];\r\ +> \n :local Error [ :tostr \$3 ];\r\ +> \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ +> ick \$Name 0 1 ] = \"\\\$\") \\\r\ +> \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ +> ror\" . \\\r\ +> \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ +> \$Error) \".\" ]);}\r\ +> \n}\r\ +> \n\r\ +> \n:global LogPrint;\r\ +> \n:global EditSchedulers;\r\ +> \n:global DevInfoIncludeIP;\r\ +> \n\r\ +> \n:local SchedInterval \"7d\";\r\ +> \n:local SchedStartTime \"03:41:27\";\r\ +> \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ +> ication to EMail.\";\r\ +> \n\r\ +> \n# Function to add or edit schedulers for scripts, to ensure they are set\ +> \_up correctly\r\ +> \n# How to use:\r\ +> \n# Call this function with the following parameters:\r\ +> \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ +> \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ +> \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ +2738,2740c3585,3652 +< \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);\ +--- +> \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ +> \_[**]\r\ +> \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ +> fault is \"/system/script { run \$SchedName; }\")) [**]\r\ +> \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ +> ff,sensitive,romon\" (default is the same)) [**]\r\ +> \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ +> *]\r\ +> \n# Notes:\r\ +> \n# * - Scheduler name is required, and is used to identify the scheduler\ +> \_to create or edit. It is usually the same as the script name (\$ScriptNa\ +> me).\r\ +> \n# ** - If optional parameters are not provided, default values will be u\ +> sed.\r\ +> \n# This allows for quick setup with minimal parameters, while still \ +> providing flexibility for customization.\r\ +> \n# Example usage:\r\ +> \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ +> ; :error true;}\r\ +> \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ +> scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ +> \_false; :error true;}\r\ +> \n\r\ +> \n:set EditSchedulers do={\r\ +> \n :local SchedName [ :tostr \$1 ];\r\ +> \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ +> \n :local Interval [ :tostr \$2 ];\r\ +> \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ +> \n :local StartTime [ :tostr \$3 ];\r\ +> \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ +> time]; }\r\ +> \n :local Comment [ :tostr \$4 ];\r\ +> \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ +> \_\$SchedName\"; }\r\ +> \n :local OnEvent [ :tostr \$5 ];\r\ +> \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ +> \$SchedName; }\"; }\r\ +> \n :local Policy [ :tostr \$6 ];\r\ +> \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ +> olicy,test,password,sniff,sensitive,romon\"; }\r\ +> \n :local StartDate [ :tostr \$7 ];\r\ +> \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ +> date]; }\r\ +> \n\r\ +> \n :onerror Err {\r\ +> \n # Check if scheduler exists, if not - create it\r\ +> \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ +> \_do={\r\ +> \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ +> SchedName . \" is NOT SET! Fix It Now!\");\r\ +> \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ +> \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ +> t-time=\$StartTime;\r\ +> \n }\r\ +> \n # Check if scheduler parameters are correct, if not - fix them\r\ +> \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ +> {\r\ +> \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ +> \n }\r\ +> \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ +> o={\r\ +> \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ +> \n }\r\ +> \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ +> ={\r\ +> \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ +> \n }\r\ +> \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ +2742,2744c3654,3929 +< \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ +< \n#:set CertName \"Starfield-Root-Certificate-Authority-G2.pem\";\r\ +< \n#/tool/fetch mode=https url=(\$AmScriptsUrl . \"certs/\" . \$CertName);\ +--- +> \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ +> \n }\r\ +> \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ +> ) do={\r\ +> \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ +> \n }\r\ +> \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ +> ) do={\r\ +> \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ +> \n }\r\ +> \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ +> \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ +> \n :return true;\r\ +> \n}\r\ +> \n\r\ +> \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ +> SchedComment ] = false) do={\r\ +> \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ +> \$ScriptName . \"!\");\r\ +> \n :set ExitOK true;\r\ +> \n :error true;\r\ +> \n}\r\ +> \n\r\ +> \n# Local Function CheckOSUpdateMode\r\ +> \n:local CheckOSUpdateMode;\r\ +> \n:set CheckOSUpdateMode do={\r\ +> \n :local NewMode [ :tostr \$1 ];\r\ +> \n :if ([ :len \$NewMode ] = 0) do={\r\ +> \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ +> alue!\");\r\ +> \n :return false;\r\ +> \n :error true;\r\ +> \n }\r\ +> \n\r\ +> \n :global BackupAndUpdateMode;\r\ +> \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ +> \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ +> \_do={\r\ +> \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ +> \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ +> ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ +> .\");\r\ +> \n :delay 5;\r\ +> \n /system/script { run \$ScriptName; };\r\ +> \n } else={\r\ +> \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ +> '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ +> \n :return false;\r\ +> \n }\r\ +> \n :return true;\r\ +> \n :error false;\r\ +> \n}\r\ +> \n\r\ +> \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ +> -partition\" ] ] > 0) do={\r\ +> \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ +> efusing to act.\");\r\ +> \n# :set ExitOK true;\r\ +> \n# :error false;\r\ +> \n# }\r\ +> \n\r\ +> \n:global EmailGeneralTo;\r\ +> \n:global BackupAndUpdateMode;\r\ +> \n:global forceBackup;\r\ +> \n:global BackupPassword;\r\ +> \n:global BackupSens;\r\ +> \n:global updateChannel;\r\ +> \n\r\ +> \n:global DevInfoIncludeIP;\r\ +> \n\r\ +> \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ +> \n# Works for `stable` and `long-term` channels.\r\ +> \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ +> 1 skipped).\r\ +> \n# Sends info if a newer (non-patch) version is found.\r\ +> \n:local installOnlyPatchUpdates false;\r\ +> \n:local scriptVersion \"26.04.30\";\r\ +> \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ +> \n#Script messages prefix\r\ +> \n:local SMP \"BkpAndUpdate:\";\r\ +> \n\r\ +> \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ +> heck logs for more details.\";\r\ +> \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ +> update\\\" v.\$scriptVersion started.\";\r\ +> \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ +> `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ +> es: `\$installOnlyPatchUpdates`\";\r\ +> \n\r\ +> \n## vv FUNCTIONS vv ##\r\ +> \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ +> \n:global MiniDateTimeStamp;\r\ +> \n:local MDTSFix [\$MiniDateTimeStamp];\r\ +> \n:global Identity;\r\ +> \n:global IdentityShort;\r\ +> \n:global devBoardName;\r\ +> \n:global runningVersion;\r\ +> \n:global runningChannel;\r\ +> \n:global devModel;\r\ +> \n:global devRbSerialNumber;\r\ +> \n:global ROsVerAvail;\r\ +> \n:global PkgUpdStatus;\r\ +> \n\r\ +> \n# Checks if two RouterOS version strings differ only by the patch versio\ +> n\r\ +> \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ +> \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ +> \n:local FuncIsPatchUpdateOnly do={\r\ +> \n :local ver1 \$1;\r\ +> \n :local ver2 \$2;\r\ +> \n # Extract the major and minor components from a version\r\ +> \n :local extractMajorMinor do={\r\ +> \n :local ver \$1;\r\ +> \n :local dot1 [:find \$ver \".\"];\r\ +> \n :if (\$dot1 = -1) do={:return \$ver}\r\ +> \n :local major [:pick \$ver 0 \$dot1];\r\ +> \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ +> \n :local dot2 [:find \$rest \".\"];\r\ +> \n :local minor \$rest;\r\ +> \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ +> \n :return (\$major . \".\" . \$minor);\r\ +> \n }\r\ +> \n\r\ +> \n# Compare the major and minor components of both version strings\r\ +> \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ +> eturn true}\r\ +> \n :return false;\r\ +> \n}\r\ +> \n# Creates backups and returns array of names\r\ +> \n# Possible arguments:\r\ +> \n# \$1 - file name, without extension\r\ +> \n# \$2 - password (optional)\r\ +> \n# \$3 - sensitive data in config (optional, default: false)\r\ +> \n# Example:\r\ +> \n#:put [\$FuncCreateBackups \$backupName]\r\ +> \n:local FuncCreateBackups do={\r\ +> \n :local backupName [ :tostr \$1 ];\r\ +> \n :local BackPass \$2;\r\ +> \n :local BackSens \$3;\r\ +> \n\r\ +> \n #Script messages prefix\r\ +> \n :local SMP \"BkpAndUpdate:\";\r\ +> \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ +> \_check logs for more details.\";\r\ +> \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ +> \_`\$backupName`\");\r\ +> \n\r\ +> \n # validate required parameter: backupName\r\ +> \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ +> {\r\ +> \n :log error \"\$SMP parameter 'backupName' is required and must be a \ +> non-empty string\";\r\ +> \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ +> t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ +> \n# :set \$backupName \"default-backup\";\r\ +> \n :error \$exitErrorMessage;\r\ +> \n }\r\ +> \n\r\ +> \n :local backupFileSys \"\$backupName.backup\";\r\ +> \n :local backupFileConfig \"\$backupName.rsc\";\r\ +> \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ +> \n\r\ +> \n ## Perform system backup\r\ +> \n :if ([:len \$BackPass] = 0) do={\r\ +> \n :log info (\"\$SMP starting backup without password, backup name: `\ +> \$backupName`\");\r\ +> \n /system backup save dont-encrypt=yes name=\$backupName;\r\ +> \n } else={\r\ +> \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ +> ckupName`\");\r\ +> \n /system backup save password=\$BackPass name=\$backupName;\r\ +> \n }\r\ +> \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ +> \n ## Export config file\r\ +> \n :if (\$BackSens = true) do={\r\ +> \n :log info (\"\$SMP starting export config with sensitive data, backu\ +> p name: `\$backupName`\");\r\ +> \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ +> port sensitive data\r\ +> \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ +> \n :execute \"/export compact terse file=\$backupName\";\r\ +> \n } else={\r\ +> \n :execute \"/export compact show-sensitive terse file=\$backupName\ +> \";\r\ +> \n }\r\ +> \n } else={\r\ +> \n :log info (\"\$SMP starting export config without sensitive data, ba\ +> ckup name: `\$backupName`\");\r\ +> \n /export compact hide-sensitive terse file=\$backupName;\r\ +> \n }\r\ +> \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ +> \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ +> \");\r\ +> \n :delay 20;\r\ +> \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ +> \n :log info (\"\$SMP system backup file successfully saved to the file\ +> \_system: `\$backupFileSys`\");\r\ +> \n } else={\r\ +> \n :log error (\"\$SMP system backup was not created, file does not exi\ +> st: `\$backupFileSys`\");\r\ +> \n :error \$exitErrorMessage;\r\ +> \n }\r\ +> \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ +> \n :log info (\"\$SMP config backup file successfully saved to the file\ +> \_system: `\$backupFileConfig`\");\r\ +> \n } else={\r\ +> \n :log error (\"\$SMP config backup was not created, file does not exi\ +> st: `\$backupFileConfig`\");\r\ +> \n :error \$exitErrorMessage;\r\ +> \n }\r\ +> \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ +> ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ +> \n :return \$backupNames;\r\ +> \n}\r\ +> \n# Sends an email\r\ +> \n# Parameters:\r\ +> \n# \$1 - to (email address)\r\ +> \n# \$2 - subject\r\ +> \n# \$3 - body\r\ +> \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ +> \n#\r\ +> \n# Example:\r\ +> \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ +> lete.\" \"backup1.backup\"\r\ +> \n:local FuncSendEmailSafe do={\r\ +> \n :global EmailGeneralTo;\r\ +> \n :local emailSubject \$2;\r\ +> \n :local emailBody \$3;\r\ +> \n :local emailAttachments \$4;\r\ +> \n :local SMP \"Bkp&Upd:\";\r\ +> \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ +> \_check logs for more details.\";\r\ +> \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ +> \n # SAFETY: wait for any previously queued email to finish\r\ +> \n :local waitTimeoutPre 60;\r\ +> \n :local waitCounterPre 0;\r\ +> \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ +> e-mail get last-status] = \"in-progress\")) do={\r\ +> \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ +> \n :log error \"\$SMP Email send aborted: previous send did not compl\ +> ete after \$waitTimeoutPre seconds\";\r\ +> \n :error \$exitErrorMessage;\r\ +> \n }\r\ +> \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ +> tool e-mail get last-status])...\";\r\ +> \n :delay 1; \r\ +> \n :set waitCounterPre (\$waitCounterPre + 1);\r\ +> \n }\r\ +> \n # Send the email\r\ +> \n :do {\r\ +> \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ +> emailBody file=\$emailAttachments;\r\ +> \n } on-error={\r\ +> \n :log error \"\$SMP Email send command failed to execute. Check logs \ +> and verify email settings.\";\r\ +> \n :error \$exitErrorMessage;\r\ +> \n }\r\ +> \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ +> s\"\r\ +> \n :local waitTimeout 60;\r\ +> \n :local waitCounter 0;\r\ +> \n :local emailStatus \"\";\r\ +> \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ +> out` seconds...\";\r\ +> \n :while (\$waitCounter < \$waitTimeout) do={\r\ +> \n :set emailStatus [/tool e-mail get last-status];\r\ +> \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ +> ng-dns\") do={\r\ +> \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ +> \n # exit loop\r\ +> \n :set waitCounter \$waitTimeout;\r\ +> \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ +> \n }\r\ +> \n # Final decision based on last status\r\ +> \n :if (\$emailStatus = \"succeeded\") do={\r\ +> \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ +2746c3931,4014 +< \n#/certificate/import file-name=\$CertName passphrase=\"\";\r\ +--- +> \n } else={\r\ +> \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ +> eck logs for more details and verify email settings.\";\r\ +> \n :error \$exitErrorMessage;\r\ +> \n }\r\ +> \n}\r\ +> \n# Global variable to track current update step\r\ +> \n# They need to be initialized here first to be available in the script\r\ +> \n:global buGlobalVarTargetOsVersion;\r\ +> \n:global buGlobalVarScriptStep;\r\ +> \n:local scriptStep \$buGlobalVarScriptStep;\r\ +> \n:do {\r\ +> \n /system/script/environment remove buGlobalVarScriptStep;\r\ +> \n} on-error={}\r\ +> \n:if ([:len \$scriptStep] = 0) do={\r\ +> \n :set scriptStep 1;\r\ +> \n}\r\ +> \n## ^^ FUNCTIONS ^^ ##\r\ +> \n#\r\ +> \n# Initial validation\r\ +> \n## Check email settings\r\ +> \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ +> \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ +> ains invalid value. Script stopped.\");\r\ +> \n :error \$exitErrorMessage;\r\ +> \n}\r\ +> \n# Values will be defined later in the script\r\ +> \n:local emailServer \"\";\r\ +> \n:local emailFromAddress [/tool e-mail get from];\r\ +> \n:log info \"\$SMP Validating email settings...\";\r\ +> \n:do {\r\ +> \n :set emailServer [/tool e-mail get server];\r\ +> \n} on-error={\r\ +> \n # This is a workaround for the RouterOS v7.12 and older versions\r\ +> \n :set emailServer [/tool e-mail get address];\r\ +> \n}\r\ +> \n:if (\$emailServer = \"0.0.0.0\") do={\r\ +> \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ +> r`, check `Tools -> Email`. Script stopped.\");\r\ +> \n :error \$exitErrorMessage;\r\ +> \n}\r\ +> \n:if ([:len \$emailFromAddress] < 3) do={\r\ +> \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ +> \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ +> \n :error \$exitErrorMessage;\r\ +> \n}\r\ +> \n# Script mode validation\r\ +> \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ +> osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ +> teMode != \"osnotify\") do={\r\ +> \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ +> set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ +> );\r\ +> \n :error \$exitErrorMessage;\r\ +> \n}\r\ +> \n# Update channel validation\r\ +> \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ +> and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ +> \_do={\r\ +> \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ +> r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ +> \n :error \$exitErrorMessage;\r\ +> \n}\r\ +> \n# Verify if script is set to install patch updates and if the update cha\ +> nnel is valid\r\ +> \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ +> updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ +> \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ +> \") do={\r\ +> \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ +> \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ +> Script stopped\");\r\ +> \n :error \$exitErrorMessage;\r\ +> \n }\r\ +> \n \r\ +> \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ +> m\") do={\r\ +> \n :log error (\"\$SMP Script is set to install only patch updates, but\ +> \_the installed RouterOS version is not from `stable` or `long-term` chann\ +> el: `\$runningChannel`. Script stopped\");\r\ +> \n :error \$exitErrorMessage;\r\ +> \n }\r\ +> \n}\r\ +> \n#\r\ +2748,2782c4016,4017 +< \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=script1 owner=vados policy=\ +< ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ +< tool/netwatch/add comment=\"doh-fallback, doh-url=https://security.cloudfl\ +< are-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Clou\ +< dFlare-doh IPv4 1\" host=1.1.1.1;\r\ +< \n/tool/netwatch/add comment=\"doh, doh-url=https://security.cloudflare-dn\ +< s.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=CloudFlare\ +< -doh IPv4 2\" host=1.0.0.1;\r\ +< \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ +< /dns.google/dns-query, doh-cert=DigiCert Global Root G2, notify, name=Goog\ +< le-doh IPv4 1\" host=8.8.8.8;\r\ +< \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ +< /dns.google/dns-query, doh-cert=DigiCert Global Root G2, notify, name=Goog\ +< le-doh IPv4 2\" host=8.8.4.4;\r\ +< \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ +< /dns.quad9.net/dns-query, doh-cert=DigiCert Global Root G3, notify, name=C\ +< uad9-doh IPv4 1\" host=9.9.9.9;\r\ +< \n/tool/netwatch/add comment=\"doh-fallback, dns-fallback, doh-url=https:/\ +< /dns.quad9.net/dns-query, doh-cert=DigiCert Global Root G3, notify, name=C\ +< uad9-doh IPv4 2\" host=149.112.112.112;\r\ +--- +> \n:local rawTime [/system clock get time];\r\ +> \n:local rawDate [/system clock get date];\r\ +2784,2787c4019,4254 +< \n/tool/netwatch/add comment=\"dns, notify, name=ns1.unet.ws\" host=91.192\ +< .189.2;" +< add dont-require-permissions=no name=script2 owner=vados policy=\ +< ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ +--- +> \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ +> \n\r\ +> \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ +> \_\$runningChannel . \"_\" . \$MDTSFix);\r\ +> \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ +> );\r\ +> \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ +> ;\r\ +> \n\r\ +> \n## Email body template\r\ +> \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ +> \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ +> (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ +> \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ +> /download/changelogs/\";\r\ +> \n:local mailBodyDeviceInfo \"\";\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ +> ;\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ +> --\");\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ +> ;\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ +> );\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ +> me\");\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ +> vRbSerialNumber\");\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ +> \$deviceOsVerAndChannelRunning\");\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ +> tem/resource/get build-time]\");\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ +> lable: \$ROsVerAvail\");\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ +> \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ +> \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ +> resource/get uptime]\");\r\ +> \n## IP address detection\r\ +> \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ +> \n :global GetPubIp;\r\ +> \n :local PublicIpAddress [ \$GetPubIp ];\r\ +> \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ +> : \" . \$PublicIpAddress . \".\");\r\ +> \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ +> \n} \r\ +> \n\r\ +> \n:local mailAttachments [:toarray \"\"];\r\ +> \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ +> \n :if (\$scriptStep = 3) do={\r\ +> \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ +> inal step.\");\r\ +> \n :delay 1m;\r\ +> \n }\r\ +> \n}\r\ +> \n\r\ +> \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ +> \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ +> s available\r\ +> \n:if (\$scriptStep = 1) do={\r\ +> \n :global BackupPassword;\r\ +> \n :global BackupSens;\r\ +> \n :local isNewOsUpdateAvailable false;\r\ +> \n :local isLatestOsAlreadyInstalled true;\r\ +> \n :local isOsNeedsToBeUpdated false;\r\ +> \n :local isUpdateCheckSucceeded false;\r\ +> \n :local isEmailNeedsToBeSent false;\r\ +> \n :local mailSubjectPartAction \"\";\r\ +> \n :local mailPtBodyAction \"\";\r\ +> \n :local mailPtSubjectBackup \"\";\r\ +> \n :local mailPtBodyBackup \"\";\r\ +> \n # Checking for new version\r\ +> \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ +> \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ +> \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ +> \n /system/package/update/set channel=\$updateChannel;\r\ +> \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ +> led version is: `\$runningVersion`\");\r\ +> \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ +> \n \r\ +> \n# /system/package/update/check-for-updates without-paging as-value;\ +> \r\ +> \n# /system/package/update check-for-updates;\r\ +> \n :delay 5s;\r\ +> \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ +> \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ +> \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ +> \n }\r\ +> \n\r\ +> \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ +> \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ +> il`\");\r\ +> \n :set isNewOsUpdateAvailable true;\r\ +> \n :set isLatestOsAlreadyInstalled false;\r\ +> \n :set isUpdateCheckSucceeded true;\r\ +> \n :set isEmailNeedsToBeSent true;\r\ +> \n :set mailSubjectPartAction \"New RouterOS available\";\r\ +> \n :set mailPtBodyAction \"New RouterOS version is available, curren\ +> t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ +> rl\";\r\ +> \n } else={\r\ +> \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ +> \n :log info (\"\$SMP No new RouterOS version is available, the lat\ +> est version is already installed: `v\$runningVersion`\");\r\ +> \n :set isUpdateCheckSucceeded true;\r\ +> \n :set mailSubjectPartAction \"No os update available\";\r\ +> \n :set mailPtBodyAction \"No new RouterOS version is available, t\ +> he latest version is already installed: `v\$runningVersion`\";\r\ +> \n } else={\r\ +> \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ +> ckage check status: `\$PkgUpdStatus`\");\r\ +> \n :set isEmailNeedsToBeSent true;\r\ +> \n :set mailSubjectPartAction \"Error unable to check new os versio\ +> n\";\r\ +> \n :set mailPtBodyAction \"An error occurred while checking for a \ +> new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ +> ew the logs on the device for more details and verify internet connectivit\ +> y.\";\r\ +> \n }\r\ +> \n }\r\ +> \n }\r\ +> \n # Checking if the script needs to install new os version\r\ +> \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ +> \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ +> \n :if (\$installOnlyPatchUpdates = true) do={\r\ +> \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ +> ue) do={\r\ +> \n :log info \"\$SMP New RouterOS version is available, and it is a\ +> \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ +> Avail\";\r\ +> \n :set isOsNeedsToBeUpdated true;\r\ +> \n } else={\r\ +> \n :log info \"\$SMP The script will not install this update, becau\ +> se it is not a patch update. Current version: v\$runningVersion, new versi\ +> on: v\$ROsVerAvail\";\r\ +> \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ +> l not be installed, because the script is set to install only patch update\ +> s.\");\r\ +> \n }\r\ +> \n } else={\r\ +> \n :set isOsNeedsToBeUpdated true;\r\ +> \n }\r\ +> \n }\r\ +> \n # Checking If the script needs to create a backup\r\ +> \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ +> sOsNeedsToBeUpdated = true) do={\r\ +> \n :log info (\"\$SMP Starting backup process.\");\r\ +> \n :set isEmailNeedsToBeSent true;\r\ +> \n :local backupName \$backupNameTemplate;\r\ +> \n # This means it's the first step where we create a backup before the\ +> \_update process\r\ +> \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ +> \n :set backupName \$backupNameBeforeUpdate;\r\ +> \n #Email body if the purpose of the script is to update the device\r\ +> \n :set mailSubjectPartAction \"Update preparation\";\r\ +> \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ +> s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ +> \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ +> el)\");\r\ +> \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ +> update will proceed only after a successful backup.\");\r\ +> \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ +> th detailed information will be sent once the update process is completed.\ +> \");\r\ +> \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ +> eive a second email within the next 10 minutes, there may be an issue. Ple\ +> ase check your device logs for further information.\");\r\ +> \n }\r\ +> \n :do {\r\ +> \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ +> sword \$BackupSens];\r\ +> \n :set mailPtSubjectBackup \"Backup created\";\r\ +> \n :set mailPtBodyBackup \"System backups have been successfully crea\ +> ted and attached to this email.\";\r\ +> \n } on-error={\r\ +> \n :set isOsNeedsToBeUpdated false;\r\ +> \n :set mailPtSubjectBackup \"Backup failed\";\r\ +> \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ +> e check device logs for more details.\";\r\ +> \n :log warning \"\$SMP Backup creation failed. Update process will b\ +> e canceled if automatic update is enabled\";\r\ +> \n }\r\ +> \n }\r\ +> \n :if (\$isEmailNeedsToBeSent = true) do={\r\ +> \n :log info \"\$SMP Preparing to send email...\";\r\ +> \n :local mailStep1Subject \$mailSubjectPrefix;\r\ +> \n :local mailStep1Body \"\";\r\ +> \n # subject\r\ +> \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ +> mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ +> \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ +> ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ +> \n # body\r\ +> \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ +> Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ +> \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ +> Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ +> \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ +> ________\\n\" . \$mailBodyCopyright);\r\ +> \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ +> \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ +> \n # Send email with backups\r\ +> \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ +> tep1Body \$mailAttachments} on-error={\r\ +> \n :set isOsNeedsToBeUpdated false;\r\ +> \n :log error \"\$SMP The script will not proceed with the update pro\ +> cess, because the email was not sent.\";\r\ +> \n }\r\ +> \n }\r\ +> \n :if ([:len \$mailAttachments] > 0) do={\r\ +> \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ +> ;\r\ +> \n /file remove \$mailAttachments;\r\ +> \n :delay 2s;\r\ +> \n }\r\ +> \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ +> \n :global OldMode;\r\ +> \n :log info \"\$SMP everything is ready to install new RouterOS, going\ +> \_to start the update process and reboot the device.\";\r\ +> \n :do {\r\ +> \n :local nextStep 2;\r\ +> \n :if (\$isCloudHostedRouter = true) do={\r\ +> \n :log info \"\$SMP The device is a cloud hosted router, the secon\ +> d step updating the Routerboard firmware will be skipped.\";\r\ +> \n :set nextStep 3;\r\ +> \n }\r\ +> \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ +> UPD-NEXT-BOOT-TASK; \\\r\ +> \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ +> etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ +> \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ +> \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ +> ledCommand start-time=startup interval=0;\r\ +> \n /system/package/update install;\r\ +> \n } on-error={\r\ +> \n # Failed to install new os version, remove the task and variables\ +2788a4256,4331 +> \n :do {\r\ +> \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ +> \n :set BackupAndUpdateMode \$OldMode;\r\ +> \n :delay 2;\r\ +> \n :do {\r\ +> \n :global buGlobalVarTargetOsVersion (a);\r\ +> \n } on-error={\r\ +> \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ +> \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ +> \n }\r\ +> \n# :if ([ :len \$OldMode ] > 0) do={\r\ +> \n# :do {\r\ +> \n# :global OldMode (a);\r\ +> \n# } on-error={\r\ +> \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ +> move global variable \\\$OldMode failed!\");\r\ +> \n# }\r\ +> \n# }\r\ +> \n } on-error={\r\ +> \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ +> e check device logs for more details. \\\r\ +> \n \\nAnd Failed to remove task and variables! Check it!\";\r\ +> \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ +> task and global variables!\");\r\ +> \n }\r\ +> \n :log error \"\$SMP Failed to install new RouterOS version. Please \ +> check device logs for more details.\";\r\ +> \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ +> ailed\");\r\ +> \n :local mailUpdateErrorBody \"The script was unable to install new \ +> RouterOS version. Please check device logs for more details.\";\r\ +> \n # Send email with error\r\ +> \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ +> ilUpdateErrorBody \"\";\r\ +> \n :error \$exitErrorMessage;\r\ +> \n }\r\ +> \n }\r\ +> \n}\r\ +> \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ +> \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ +> und\r\ +> \n:if (\$scriptStep = 2) do={\r\ +> \n :log info \"\$SMP The script is in the second step, updating Routerboa\ +> rd firmware.\";\r\ +> \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ +> entFw to v.\$deviceRbUpgradeFw\";\r\ +> \n /system routerboard upgrade;\r\ +> \n :delay 2;\r\ +> \n :log info \"\$SMP routerboard upgrade process was completed, going to \ +> reboot in a moment!\";\r\ +> \n ## Set task to send final report on the next boot\r\ +> \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ +> :global buGlobalVarScriptStep 3; \\\r\ +> \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ +> \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ +> \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ +> me=startup interval=0;\r\ +> \n :delay 2;\r\ +> \n /system reboot;\r\ +> \n}\r\ +> \n\r\ +> \n## STEP 3: Final report (after second reboot, with delay).\r\ +> \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ +> und.\r\ +> \n:if (\$scriptStep = 3) do={\r\ +> \n :log info (\"\$SMP The script is in the third step, sending final repo\ +> rt.\");\r\ +> \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ +> \n :do {\r\ +> \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ +> ersion\"];\r\ +> \n :global buGlobalVarTargetOsVersion (a);\r\ +> \n } on-error={\r\ +> \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ +> al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ +> \n }\r\ +2790,2795c4333,4506 +< \n:global InspectVar;\r\ +< \n#:global RouterBoard;\r\ +< \n#:do {:set RouterBoard [[ :parse \"/system/routerboard/get\" ]]} on-erro\ +< r={}\r\ +< \n#:global Resources;\r\ +< \n#:do {:set Resources [[ :parse \"/system/resource/get\" ]]} on-error={}\ +--- +> \n :if ([ :len \$OldMode ] != 0) do={\r\ +> \n :do {\r\ +> \n# /system/script/environment remove [find name=\"OldMode\"];\r\ +> \n :global OldMode (a);\r\ +> \n } on-error={\r\ +> \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ +> \_global variable \\\$OldMode failed!\");\r\ +> \n }\r\ +> \n }\r\ +> \n :if ([:len \$targetOsVersion] = 0) do={\r\ +> \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ +> t the target updated OS version from the global variable.\";\r\ +> \n }\r\ +> \n :local mailStep3Subject \$mailSubjectPrefix;\r\ +> \n :local mailStep3Body \"\";\r\ +> \n :if (\$targetOsVersion = \$runningVersion) do={\r\ +> \n :log info \"\$SMP Successfully verified new RouterOS version: target\ +> : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ +> \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ +> Backup created\");\r\ +> \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ +> rade process was completed\");\r\ +> \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ +> \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ +> \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ +> of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ +> yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ +> \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ +> BackupPassword \$BackupSens];\r\ +> \n } else={\r\ +> \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ +> \$targetOsVersion`, current: `\$runningVersion`\";\r\ +> \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ +> \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ +> ify that the new RouterOS version was installed, target version: `\$target\ +> OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ +> re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ +> \n }\r\ +> \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ +> y \$mailAttachments;\r\ +> \n :if ([:len \$mailAttachments] > 0) do={\r\ +> \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ +> ;\r\ +> \n /file remove \$mailAttachments;\r\ +> \n :delay 2;\r\ +> \n }\r\ +> \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ +> t has finished.\");\r\ +> \n}\r\ +> \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ +> \\n\\n\");\r\ +> \n\r\ +> \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ +> \n" +> add comment="download, import and update firewall address-lists" \ +> dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ +> policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> source="#!rsc by Vados\r\ +> \n# RouterOS script: AM-FwAddrLists\r\ +> \n# Script comment: download, import and update firewall address-lists\r\ +> \n# Copyright (c) 2007-2026 Vados \r\ +> \n#\r\ +> \n#\r\ +> \n# requires RouterOS, version=7.19\r\ +> \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ +> \n:local ExitOK false;\r\ +> \n:onerror Err {\r\ +> \n :global GlobalConfReady; :global GlobalFuncReady;\r\ +> \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ +> \\\r\ +> \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ +> delay=500ms max=50;\r\ +> \n :local ScriptName [ :jobname ];\r\ +> \n\r\ +> \n :global GlobalScriptsUrl;\r\ +> \n :local FwAddrListsUrl (\$GlobalScriptsUrl . \"fw-addr-lists.d/\");\r\ +> \n\r\ +> \n :local FwAddrLists {\r\ +> \n \"allow\"={\r\ +> \n { url=(\$FwAddrListsUrl . \"allow\");\r\ +> \n cert=\"ISRG Root X2\"; timeout=1w };\r\ +> \n };\r\ +> \n \"block\"={\r\ +> \n { url=(\$FwAddrListsUrl . \"block\");\r\ +> \n cert=\"ISRG Root X2\" };\r\ +> \n# { url=\"https://raw.githubusercontent.com/stamparm/ipsum/refs/hea\ +> ds/master/levels/4.txt\";\r\ +> \n# # higher level (decrease the numerical value) for more addresses, \ +> and vice versa\r\ +> \n# cert=\"ISRG Root X1\" };\r\ +> \n# { url=\"https://www.dshield.org/block.txt\"; cidr=\"/24\";\r\ +> \n# cert=\"GTS Root R4\" };\r\ +> \n# { url=\"https://lists.blocklist.de/lists/strongips.txt\";\r\ +> \n# cert=\"GTS Root R4\" };\r\ +> \n# { url=\"https://www.spamhaus.org/drop/drop_v4.json\";\r\ +> \n# cert=\"GTS Root R4\" };\r\ +> \n# { url=\"https://www.spamhaus.org/drop/drop_v6.json\";\r\ +> \n# cert=\"GTS Root R4\" };\r\ +> \n };\r\ +> \n# \"mikrotik\"={\r\ +> \n# { url=(\$FwAddrListsUrl . \"mikrotik\");\r\ +> \n# cert=\"ISRG Root X2\"; timeout=1w };\r\ +> \n# };\r\ +> \n \"FuckRKN\"={\r\ +> \n { url=(\$FwAddrListsUrl . \"FuckRKN\");\r\ +> \n cert=\"ISRG Root X2\"; timeout=1w };\r\ +> \n };\r\ +> \n };\r\ +> \n :local FwAddrListTimeOut 1d;\r\ +> \n\r\ +> \n :global CertificateAvailable;\r\ +> \n :global EitherOr;\r\ +> \n :global FetchHuge;\r\ +> \n :global HumanReadableNum;\r\ +> \n :global IfThenElse;\r\ +> \n :global LogPrint;\r\ +> \n\r\ +> \n :global NetMask4;\r\ +> \n :global NetMask6;\r\ +> \n :global ScriptLock;\r\ +> \n \r\ +> \n :local FindDelim do={\r\ +> \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ +> jklmnopqrstuvwxyz-\";\r\ +> \n :for I from=0 to=[ :len \$1 ] do={\r\ +> \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ +> ] != \"num\") do={\r\ +> \n :return \$I;\r\ +> \n }\r\ +> \n }\r\ +> \n }\r\ +> \n\r\ +> \n :local GetBranch do={\r\ +> \n :global EitherOr;\r\ +> \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ +> therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ +> \n }\r\ +> \n \r\ +> \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ +> \n :set ExitOK true;\r\ +> \n :error false;\r\ +> \n }\r\ +> \n\r\ +> \n :if ([ :len [ /log/find where topics=({\"script\"; \"warning\"}) \\\r\ +> \n message=(\"\\\$LogPrint: The message is already in log, scripting \ +> subsystem may have crashed before!\") ] ] > 0) do={\r\ +> \n :log warning (\$ScriptName . \" Scripting subsystem may have crashed\ +> , possibly caused by us. Delaying!\");\r\ +> \n :delay 5m;\r\ +> \n }\r\ +> \n\r\ +> \n :local ListComment (\"managed by \" . \$ScriptName);\r\ +> \n\r\ +> \n :foreach FwListName,FwList in=\$FwAddrLists do={\r\ +> \n :local CntAdd 0;\r\ +> \n :local CntRenew 0;\r\ +> \n :local CntRemove 0;\r\ +> \n :local IPv4Addresses ({});\r\ +> \n :local IPv6Addresses ({});\r\ +> \n :local Failure false;\r\ +> \n\r\ +> \n :foreach List in=\$FwList do={\r\ +> \n :local CheckCertificate false;\r\ +> \n :local Data false;\r\ +> \n :local TimeOut [ \$EitherOr [ :totime (\$List->\"timeout\") ] \$Fw\ +> AddrListTimeOut ];\r\ +> \n\r\ +> \n :if ([ :len (\$List->\"cert\") ] > 0) do={\r\ +> \n :set CheckCertificate true;\r\ +> \n :if ([ \$CertificateAvailable (\$List->\"cert\") \"fetch\" ] = f\ +> alse) do={\r\ +> \n :log warning (\$ScriptName . \" Downloading required certifica\ +> te (\" . \$FwListName . \\\r\ +> \n \" / \" . \$List->\"url\" . \") failed, trying anyway.\");\ +2796a4508,4509 +> \n }\r\ +> \n }\r\ +2798,2800c4511,4524 +< \n#:global Updates;\r\ +< \n#:do {:set Updates [[ :parse \"/system/package/update/get\" ]]} on-error\ +< ={}\r\ +--- +> \n :for I from=1 to=5 do={\r\ +> \n :if (\$Data = false) do={\r\ +> \n :set Data [ :tolf [ \$FetchHuge \$ScriptName (\$List->\"url\")\ +> \_\$CheckCertificate ] ];\r\ +> \n :if (\$Data = false) do={\r\ +> \n :if (\$I < 5) do={\r\ +> \n :log debug (\$ScriptName . \" Failed downloading for list \ +> '\" . \$FwListName . \\\r\ +> \n \"', \" . \$I . \". try from: \" . \$List->\"url\");\r\ +> \n :delay ((\$I * \$I) . \"s\");\r\ +> \n }\r\ +> \n }\r\ +> \n }\r\ +> \n }\r\ +2802,2805c4526,4537 +< \n#:global PkgUpdStatus \"undefined\";\r\ +< \n#:local Update [/system/package/update/get];\r\ +< \n#:retry command={:set PkgUpdStatus (\$Update->\"status\")} delay=200ms m\ +< ax=5 on-error={};\r\ +--- +> \n :if (\$Data = false) do={\r\ +> \n :set Data \"\";\r\ +> \n :set Failure true;\r\ +> \n :log warning (\$ScriptName . \" Failed downloading for list '\" \ +> . \$FwListName . \\\r\ +> \n \"' from: \" . \$List->\"url\");\r\ +> \n } else={\r\ +> \n :log debug (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ +> um [ :len \$Data ] 1024 ] . \\\r\ +> \n \"B for list '\" . \$FwListName . \"' from: \" . \$List->\"u\ +> rl\");\r\ +> \n }\r\ +2807,2808c4539,4597 +< \n#:log warning [\$InspectVar \$RouterBoard];\r\ +< \n#:put [\$InspectVar \$RouterBoard];\r\ +--- +> \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv \ +> options=dsv.plain ] do={\r\ +> \n :set Line (\$Line->0);\r\ +> \n :local Address;\r\ +> \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Li\ +> ne ] - 1) ] = \"}\") do={\r\ +> \n :do {\r\ +> \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"\ +> cidr\") ];\r\ +> \n } on-error={ }\r\ +> \n } else={\r\ +> \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ +> ist->\"cidr\"));\r\ +> \n }\r\ +> \n :do {\r\ +> \n :local Branch;\r\ +> \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\ +> \\.[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ +> \n :local Net \$Address;\r\ +> \n :local CIDR 32;\r\ +> \n :local Slash [ :find \$Address \"/\" ];\r\ +> \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ +> \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ +> \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address\ +> \_] ];\r\ +> \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$C\ +> IDR ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ +> \n }\r\ +> \n :set Branch [ \$GetBranch \$Address ];\r\ +> \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ +> \n :error true;\r\ +> \n }\r\ +> \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3\ +> })\?\\\$\") do={\r\ +> \n :local Net \$Address;\r\ +> \n :local CIDR 128;\r\ +> \n :local Slash [ :find \$Address \"/\" ];\r\ +> \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ +> \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ +> \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address\ +> \_] ];\r\ +> \n }\r\ +> \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \ +> \"/\" . \$CIDR);\r\ +> \n :set Branch [ \$GetBranch \$Address ];\r\ +> \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ +> \n :error true;\r\ +> \n }\r\ +> \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\")\ +> \_do={\r\ +> \n :set Branch [ \$GetBranch \$Address ];\r\ +> \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ +> \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ +> \n :error true;\r\ +> \n }\r\ +> \n } on-error={:log error (\$ScriptName . \" Failed parsing address\ +> : \" . \$Line->0);}\r\ +> \n }\r\ +> \n }\r\ +2810,2811c4599,4622 +< \n#:retry command={/system/package/update check-for-updates without-paging\ +< \_as-value} delay=300ms max=5 on-error={};\r\ +--- +> \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ +> \n list=\$FwListName comment=\$ListComment ] do={\r\ +> \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ +> \r\ +> \n :local Branch [ \$GetBranch \$Address ];\r\ +> \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ +> \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ +> \n :log debug (\$ScriptName . \" Renewing IPv4 address \" . \$Addre\ +> ss . \\\r\ +> \n \" in list '\" . \$FwListName . \"' with \" . \$TimeOut . \"\ +> .\");\r\ +> \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ +> \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ +> \n :set CntRenew (\$CntRenew + 1);\r\ +> \n } else={\r\ +> \n :if (\$Failure = false) do={\r\ +> \n :log debug (\$ScriptName . \" Removing IPv4 address \" . \$Add\ +> ress . \\\r\ +> \n \" from list '\" . \$FwListName . \".\");\r\ +> \n /ip/firewall/address-list/remove \$Entry;\r\ +> \n :set CntRemove (\$CntRemove + 1);\r\ +> \n }\r\ +> \n }\r\ +> \n }\r\ +2813,2814c4624,4650 +< \n#:global arrDeviceInfo ({});\r\ +< \n#:local arrDeviceInfo ({\"section\":{\"key\":\"\"; \"value\":\"\";};});\ +--- +> \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ +> \n list=\$FwListName comment=\$ListComment ] do={\r\ +> \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ +> ;\r\ +> \n :local Branch [ \$GetBranch \$Address ];\r\ +> \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ +> \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ +> \n :log debug (\$ScriptName . \" Renewing IPv6 address \" . \$Addre\ +> ss . \\\r\ +> \n \" in list '\" . \$FwListName . \"' with \" . \$TimeOut . \"\ +> .\");\r\ +> \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ +> \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ +> \n :set CntRenew (\$CntRenew + 1);\r\ +> \n } else={\r\ +> \n :if (\$Failure = false) do={\r\ +> \n :log debug (\$ScriptName . \" Removing IPv6 address \" . \$Add\ +> ress . \\\r\ +> \n \" from list '\" . \$FwListName .\".\");\r\ +> \n /ipv6/firewall/address-list/remove \$Entry;\r\ +> \n :set CntRemove (\$CntRemove + 1);\r\ +> \n }\r\ +> \n }\r\ +> \n }\r\ +> \n\r\ +> \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ +> \n :log debug (\$ScriptName . \" Handling branch: \" . \$BranchName);\ +2816,2823c4652,4668 +< \n#:set (\$arrDeviceInfo->\"RouterBoard\") [[ :parse \"/system/routerboard\ +< /get\" ]];\r\ +< \n#:retry command={:set (\$arrDeviceInfo->\"Updates\") [[ :parse \"/system\ +< /package/update/get\" ]]} delay=500ms max=5 on-error={:log error (\"Error\ +< \")};\r\ +< \n# [:toarray {\"section\"={\"key\"=\"\"; \"value\"=\"\";} }];\r\ +< \n# [:toarray \"\"];\r\ +< \n:local arrDeviceInfo ({});\r\ +--- +> \n :foreach Address,Timeout in=\$Branch do={\r\ +> \n :log debug (\$ScriptName . \" Adding IPv4 address \" . \$Address\ +> \_. \\\r\ +> \n \" to list '\" . \$FwListName . \"' with \" . \$Timeout . \"\ +> .\");\r\ +> \n :onerror Err {\r\ +> \n /ip/firewall/address-list/add list=\$FwListName comment=\$List\ +> Comment \\\r\ +> \n address=\$Address timeout=\$Timeout;\r\ +> \n :set CntAdd (\$CntAdd + 1);\r\ +> \n } do={\r\ +> \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ +> . \$Address . \\\r\ +> \n \" to list '\" . \$FwListName . \"': \" . \$Err);\r\ +> \n }\r\ +> \n }\r\ +> \n }\r\ +2825,2828c4670,4700 +< \n:set (\$arrDeviceInfo->\"devUpdates\") {\"channel\"; \"stable\";};\r\ +< \n#:do {:set (arrDeviceInfo->\"Updates\") [[ :parse \"/system/package/upda\ +< te/get\" ]]} on-error={:log error (\$error)}\r\ +< \n\$InspectVar \$arrDeviceInfo;\r\ +--- +> \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ +> \n :log debug (\$ScriptName . \" Handling branch: \" . \$BranchName);\ +> \r\ +> \n :foreach Address,Timeout in=\$Branch do={\r\ +> \n :log debug (\$ScriptName . \" Adding IPv6 address \" . \$Address\ +> \_. \\\r\ +> \n \" to list '\" . \$FwListName . \"' with \" . \$Timeout . \"\ +> .\");\r\ +> \n :onerror Err {\r\ +> \n /ipv6/firewall/address-list/add list=\$FwListName comment=\$Li\ +> stComment \\\r\ +> \n address=\$Address timeout=\$Timeout;\r\ +> \n :set CntAdd (\$CntAdd + 1);\r\ +> \n } do={\r\ +> \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ +> . \$Address . \\\r\ +> \n \" to list '\" . \$FwListName . \"': \" . \$Err);\r\ +> \n }\r\ +> \n }\r\ +> \n }\r\ +> \n\r\ +> \n :log info (\$ScriptName . \" list: \" . \$FwListName . \\\r\ +> \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ +> \")\" . \\\r\ +> \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ +> \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ +> \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ +> \n }\r\ +> \n} do={\r\ +> \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ +> \n}\r\ +2833,2865d4704 +< /interface bridge +< add admin-mac=48:8F:5A:8E:1A:BC auto-mac=no mlag-peer-port="ether1 - Unet" \ +< name=Bridge-LAN port-cost-mode=short +< /interface wireless +< # managed by CAPsMAN +< # channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +< set [ find default-name=wlan1 ] ampdu-priorities=0,1,2,3,4,5,6,7 band=\ +< 2ghz-b/g/n basic-rates-a/g=\ +< 6Mbps,9Mbps,12Mbps,18Mbps,24Mbps,36Mbps,48Mbps,54Mbps basic-rates-b=\ +< 1Mbps,2Mbps,5.5Mbps,11Mbps channel-width=20/40mhz-XX country=belarus \ +< disabled=no frequency=auto hw-protection-mode=rts-cts mode=ap-bridge \ +< rate-set=configured skip-dfs-channels=10min-cac ssid=vados \ +< wds-default-bridge=Bridge-LAN wds-mode=dynamic-mesh wireless-protocol=\ +< 802.11 wmm-support=enabled wps-mode=disabled +< # managed by CAPsMAN +< # channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +< set [ find default-name=wlan2 ] ampdu-priorities=0,1,2,3,4,5,6,7 band=\ +< 5ghz-a/n/ac basic-rates-a/g=\ +< 6Mbps,9Mbps,12Mbps,18Mbps,24Mbps,36Mbps,48Mbps,54Mbps channel-width=\ +< 20/40/80mhz-XXXX country=belarus disabled=no frequency=auto \ +< hw-protection-mode=rts-cts mode=ap-bridge rate-set=configured \ +< skip-dfs-channels=10min-cac ssid=vados5g wds-default-bridge=Bridge-LAN \ +< wds-mode=dynamic-mesh wireless-protocol=802.11 wmm-support=enabled \ +< wps-mode=disabled +< /interface wireless nstreme +< # managed by CAPsMAN +< # channel: 2422/20-Ce/gn(18dBm), SSID: vados, local forwarding +< set wlan1 enable-nstreme=yes +< # managed by CAPsMAN +< # channel: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +< set wlan2 enable-nstreme=yes +< /caps-man manager +< set enabled=yes +2874,2875c4713,4717 +< set builtin-trust-store="ipsec,wpa-eap,capsman,fetch,sstp,ovpn,radius,userman,\ +< openflow,tr069,dot1x,www,api,reverse-proxy" crl-store=system +--- +> set builtin-trust-store=fetch +> /disk settings +> set auto-media-interface=lo default-mount-point-template=usb1 +> /ip smb +> set comment=MikroVados domain=WORKGROUP enabled=yes interfaces=Bridge-LAN +2877,2883c4719,4726 +< add bridge=Bridge-LAN disabled=yes interface="ether1 - Unet" +< 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=Bridge-LAN ingress-filtering=no interface=ether2 \ +> internal-path-cost=10 path-cost=10 +> add bridge=Bridge-LAN comment=LEN ingress-filtering=no interface=ether3 \ +> internal-path-cost=10 path-cost=10 +> add bridge=Bridge-LAN comment=NUC ingress-filtering=no interface=ether4 \ +> internal-path-cost=10 path-cost=10 +> add bridge=Bridge-LAN ingress-filtering=no interface=ether5 \ +> internal-path-cost=10 path-cost=10 +2885d4727 +< # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +2888d4729 +< # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +2891d4731 +< # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +2894,2896c4734 +< # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +< set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ +< forward=no max-neighbor-entries=8192 +--- +> set disable-ipv6=yes max-neighbor-entries=8192 +2903c4741 +< add interface="ether1 - Unet" list=WAN +--- +> add comment=WAN interface="ether1 - Unet" list=WAN +2907,2918c4745,4760 +< /interface wifi configuration +< add channel=2G-AX datapath=xs-DataPath disabled=no mode=ap name=2G-AX \ +< security=sec2g security.ft=yes .ft-over-ds=yes ssid=vados +< add channel=2G-N datapath=xs-DataPath disabled=no mode=ap name=2G-N security=\ +< sec2g security.ft=yes .ft-over-ds=yes ssid=vados +< add channel=5G-AC datapath=xs-DataPath disabled=no mode=ap name=5G-AC \ +< security=sec5G security.authentication-types="" .ft=yes .ft-over-ds=yes \ +< ssid=vados +< add channel=2G-G datapath=xs-DataPath disabled=no mode=ap name=2G-G security=\ +< sec2g security.authentication-types="" .ft=yes .ft-over-ds=yes ssid=vados +< /interface wifi datapath +< add bridge=Bridge-LAN disabled=no interface-list=all name=xs-DataPath +--- +> /interface wifi cap +> set caps-man-addresses=10.30.30.10 certificate=request discovery-interfaces=\ +> wlan1,wlan2 lock-to-caps-man=no slaves-static=no +> /interface wifi capsman +> set package-path="" require-peer-certificate=no upgrade-policy=none +> /interface wifi provisioning +> add action=create-dynamic-enabled disabled=no master-configuration=2G-N \ +> name-format=2G-%I slave-configurations=2G-G supported-bands=\ +> 2ghz-n,2ghz-g,2ghz-g +> add action=create-dynamic-enabled disabled=no master-configuration=5G-AC \ +> name-format=5G-%I supported-bands=5ghz-ac,5ghz-ac +> /interface wireguard peers +> add allowed-address=10.8.0.2/32,0.0.0.0/0 client-allowed-address=0.0.0.0/0 \ +> endpoint-address=89.110.101.17 endpoint-port=29185 interface=WG name=\ +> MikroVados persistent-keepalive=10s public-key=\ +> "64Y25EUZQQt8EIeAJYD8qWt+te8qvGEepnswdBgmzlY=" +2923c4765 +< CAP-488F5A8E1ABC discovery-interfaces=ether2 enabled=yes interfaces=\ +--- +> CAP-18FD746E5814 discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ +2924a4767,4768 +> /interface wireless sniffer +> set file-name=24g +2926a4771 +> add address=10.8.0.2/24 interface=WG network=10.8.0.0 +2928,2932c4773,4775 +< add interface="ether1 - Unet" name=client1 +< /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 +--- +> add add-default-route=special-classless default-route-distance=2 \ +> default-route-tables=main interface="ether1 - Unet" name="ether1 - Unet" \ +> use-peer-dns=no +2956,2957d4798 +< 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 +2964,2965d4804 +< 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 +2976,2977c4815,4817 +< 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.74 client-id=1:20:72:a9:d3:34:36 dhcp-option=HostName \ +> dhcp-option-set=MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP \ +> use-src-mac=yes +2982a4823,4824 +> add address=10.30.30.86 dhcp-option=HostName dhcp-option-set=MikroYellow \ +> mac-address=48:8F:5A:8E:1A:C2 server=masterDHCP +2984,2987c4826,4829 +< add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ +< domain-search-list dhcp-option-set=domain-search-set dns-server=\ +< 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ +< 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +--- +> add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=HostName \ +> dhcp-option-set=domain-search-set dns-server=10.30.30.81 domain=WORKGROUP \ +> gateway=10.30.30.81 netmask=24 ntp-server=10.30.30.81,10.30.30.10 \ +> wins-server=10.30.30.95,10.30.30.98 +2992,2993d4833 +< add address=10.30.30.33 name=ros.vados.ru type=A +< add address=10.30.30.95 name=vados-nuc type=A +3000a4841 +> add address=10.30.30.33 name=git.vados.ru type=A +3001a4843 +> add address=10.30.30.33 match-subdomain=yes name=ros.vados.ru type=A +3009,3010d4850 +< add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" name=\ +< security.cloudflare-dns.com type=A +3012a4853,4854 +> add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" name=\ +> security.cloudflare-dns.com type=A +3015,3016c4857,4858 +< add address=149.112.112.112 comment="Cuad9-dns IPv4 2" 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 +3026,3029c4868,4871 +< add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ +< dns.quad9.net type=A +< add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ +< A +--- +> add address=2606:4700:4700::1111 disabled=yes name=\ +> security.cloudflare-dns.com type=AAAA +> add address=2606:4700:4700::1001 disabled=yes name=\ +> security.cloudflare-dns.com type=AAAA +3638,3639c5480,5481 +< add address=10.30.30.33 comment=NUC list=NoWG +< add address=10.30.30.10 comment=MikroNUC list=NoWG +--- +> add address=10.30.30.33 comment=amster-nuc disabled=yes list=NoWG +> add address=10.30.30.11 comment=MikroNUC list=NoWG +3647,3650c5489,5497 +< 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=10.30.30.95 comment=NUC list=NoWG +> add address=glb.tas02.cws-prod.dcat.dsp.mp.microsoft.com. comment=\ +> tas02.cws.update.microsoft.com list=windowsupdateslist +> add address=glb.cwsapp.prod.dcat.dsp.trafficmanager.net. comment=\ +> glb.tas01.cwsapp-prod.dcat.dsp.mp.microsoft.com list=windowsupdateslist +> add address=glb.tas01.cwsapp-prod.dcat.dsp.mp.microsoft.com. comment=\ +> tas01.cwsapp.update.microsoft.com list=windowsupdateslist +> add address=disc601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ +> disc601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +3652,3655c5499,5518 +< add action=accept chain=input protocol=icmp +< add action=accept chain=input connection-state=established +< add action=accept chain=input connection-state=related +< add action=drop chain=input in-interface-list=!LAN +--- +> 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 +> 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 +> 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,2222 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=5d chain=input comment="block honeypot NTP" \ +> connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ +> udp src-address=!10.30.30.0/24 +3658a5522,5525 +> add action=accept chain=input comment="accept WireGuard" dst-port=13231 \ +> in-interface="ether1 - Unet" protocol=udp src-address-list=VPN-Server +> add action=accept chain=input disabled=yes dst-port=443 in-interface=\ +> "ether1 - Unet" protocol=tcp +3685,3691c5552 +< connection-state=invalid +< add action=accept chain=input connection-state=established disabled=yes +< add action=accept chain=input connection-state=related disabled=yes +< add action=drop chain=input disabled=yes in-interface-list=!LAN +< add action=drop chain=forward comment=\ +< "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ +< facebook +--- +> connection-state=invalid disabled=yes +3693,3724c5554,5555 +< "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ +< dst-address-list=YouTube +< add action=accept chain=input comment=\ +< "accept NTP clients on local area network" dst-port=123 in-interface=\ +< Bridge-LAN in-interface-list=LAN protocol=udp +< add action=accept chain=output disabled=yes protocol=tcp src-port=443 +< 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=fasttrack-connection chain=forward comment="defconf: fasttrack" \ +< connection-state=established,related,new disabled=yes routing-mark=\ +< !To-VPN +< 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 ICMP" disabled=yes \ +< protocol=icmp +< add action=accept chain=input comment=\ +< "defconf: accept to local loopback (for CAPsMAN)" disabled=yes \ +< dst-address=127.0.0.1 +< add action=accept chain=output disabled=yes src-address=127.0.0.1 +< add action=accept chain=forward comment="defconf: accept in ipsec policy" \ +< ipsec-policy=in,ipsec +< add action=accept chain=forward comment="defconf: accept out ipsec policy" \ +< 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 +--- +> "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ +> connection-state=new in-interface="ether1 - Unet" +3733a5565,5567 +> add action=fasttrack-connection chain=forward comment=\ +> "FastTrack main traffic" connection-state=established,related disabled=\ +> yes +3734a5569,5571 +> add action=mark-connection chain=prerouting comment=\ +> "Mark VPN connections to exclude from FastTrack" in-interface=WG \ +> log-prefix=Mark-VPN-FT new-connection-mark=VPN_No_FastTrack +3750,3769c5587,5588 +< add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ +< dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina log-prefix=VPN-all \ +< new-routing-mark=To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +< add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ +< 10.8.0.0/24 dst-address-list=!NoVdsina log=yes log-prefix=VPN-Net \ +< new-routing-mark=To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +< add action=mark-routing chain=prerouting comment="Test sites" disabled=yes \ +< dst-address=!10.30.30.0/24 dst-address-list=Test new-routing-mark=To-VPN \ +< passthrough=no src-address=10.30.30.0/24 +< add action=mark-routing chain=prerouting comment=Instagram dst-address=\ +< !10.30.30.0/24 dst-address-list=Instagram new-routing-mark=To-VPN \ +< src-address=10.30.30.0/24 src-address-list=!NoWG +< add action=mark-routing chain=prerouting comment=Twitter dst-address=\ +< !10.30.30.0/24 dst-address-list=Twitter new-routing-mark=To-VPN \ +< src-address=10.30.30.0/24 src-address-list=!NoWG +< add action=mark-routing chain=prerouting comment=YouTube dst-address=\ +< !10.30.30.0/24 dst-address-list=YouTube new-routing-mark=To-VPN \ +< src-address=10.30.30.0/24 src-address-list=!NoWG +< add action=mark-routing chain=prerouting comment=facebook dst-address=\ +< !10.30.30.0/24 dst-address-list=facebook new-routing-mark=To-VPN \ +--- +> add action=mark-routing chain=prerouting comment=Telegram dst-address=\ +> !10.30.30.0/24 dst-address-list=Telegram new-routing-mark=To-VPN \ +3773a5593,5594 +> add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ +> out-interface=WG protocol=tcp tcp-flags=syn tcp-mss=1381-65535 +3778,3789d5598 +< add action=masquerade chain=srcnat out-interface-list=WAN +< add action=redirect chain=dstnat comment=\ +< "FORCE DNS TO LOCAL MIKROTIK DNS SERVER" dst-port=53 protocol=tcp \ +< src-address=10.30.30.0/24 to-ports=53 +< add action=redirect chain=dstnat dst-port=53 protocol=udp src-address=\ +< 10.30.30.0/24 to-ports=53 +< 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 +3794a5604,5619 +> add action=masquerade chain=srcnat comment="masquerade Default" ipsec-policy=\ +> out,none out-interface="ether1 - Unet" routing-mark=!To-VPN +> add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ +> in-interface=Bridge-LAN log-prefix=WG-nat out-interface=WG routing-mark=\ +> To-VPN +> add action=dst-nat chain=dstnat comment=npm disabled=yes dst-port=81 \ +> in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ +> to-ports=81 +> add action=dst-nat chain=dstnat comment=amster.vados.ruhttps disabled=yes \ +> dst-port=443 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ +> 10.30.30.33 to-ports=443 +> add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ +> disabled=yes dst-port=80 in-interface="ether1 - Unet" protocol=tcp \ +> to-addresses=10.30.30.33 to-ports=80 +> add action=dst-nat chain=dstnat disabled=yes dst-port=80 in-interface=\ +> "ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 to-ports=80 +3800a5626,5635 +> add action=dst-nat chain=dstnat dst-address=146.120.64.211 dst-port=32000 \ +> in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.95 \ +> to-ports=32000 +> add action=dst-nat chain=dstnat dst-address=146.120.64.211 dst-port=32000 \ +> in-interface="ether1 - Unet" protocol=udp to-addresses=10.30.30.95 \ +> to-ports=32000 +> add action=dst-nat chain=dstnat dst-port=32000 protocol=tcp routing-mark=\ +> To-VPN to-addresses=10.30.30.95 to-ports=32000 +> add action=dst-nat chain=dstnat dst-port=32000 protocol=udp routing-mark=\ +> To-VPN to-addresses=10.30.30.95 to-ports=32000 +3801a5637,5638 +> add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ +> in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +3870,3941c5707 +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=\ +< api.telegram.org content=api.telegram.org +< 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=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=vk.com \ +< content=vk.com +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=ok.ru content=\ +< ok.ru +< add action=add-dst-to-address-list address-list=Instagram \ +< address-list-timeout=none-dynamic chain=prerouting comment=Instagram \ +< content=instagram.com +< add action=add-dst-to-address-list address-list=Twitter address-list-timeout=\ +< none-dynamic chain=prerouting comment=Twitter content=twimg.com +< add action=add-dst-to-address-list address-list=Twitter address-list-timeout=\ +< none-dynamic chain=prerouting content=x.com +< add action=add-dst-to-address-list address-list=Twitter address-list-timeout=\ +< none-dynamic chain=prerouting content=twitter.com +< add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ +< none-dynamic chain=prerouting comment=YouTube content=youtube.com +< add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ +< none-dynamic chain=prerouting content=googleapis.com +< add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ +< none-dynamic chain=prerouting content=ggpht.com +< add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ +< none-dynamic chain=prerouting content=youtu.be +< add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ +< none-dynamic chain=prerouting content=ytimg.com +< add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ +< none-dynamic chain=prerouting content=googlevideo.com +< add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ +< none-dynamic chain=prerouting content=gstatic.com +< add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ +< none-dynamic chain=prerouting content=googleusercontent.com +< add action=add-dst-to-address-list address-list=YouTube address-list-timeout=\ +< none-dynamic chain=prerouting content=nhacmp3youtube.com +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ +< content=rutube.ru +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=me.com \ +< content=me.com +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=icloud.com \ +< content=icloud.com +< add action=add-dst-to-address-list address-list=facebook \ +< address-list-timeout=none-dynamic chain=prerouting comment=facebook \ +< content=fbcd.net +< add action=add-dst-to-address-list address-list=facebook \ +< address-list-timeout=none-dynamic chain=prerouting content=facebook.com +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=comp-lenta.ru \ +< content=comp-lenta.ru +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=\ +< smtp.mail.me.com content=smtp.mail.me.com log=yes +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=gov.ru \ +< content=gov.ru +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=\ +< r78.fssp.gov.ru content=r78.fssp.gov.ru +< add action=add-dst-to-address-list address-list=NoVdsina \ +< address-list-timeout=none-dynamic chain=prerouting comment=api.db-ip.com \ +< content=api.db-ip.com +< add action=add-dst-to-address-list address-list=NoVdsina \ +--- +> add action=add-dst-to-address-list address-list=Telegram \ +3951,3952d5716 +< /ip hotspot user profile +< set [ find default=yes ] address-pool=dhcp +3955d5718 +< add server=*1 +3966c5729,5734 +< add disabled=yes dst-address=0.0.0.0/0 gateway=10.30.30.81 +--- +> add disabled=no distance=3 dst-address=192.168.0.0/24 gateway=l2tp-vpn +> add disabled=no distance=4 dst-address=192.168.5.0/24 gateway=l2tp-vpn +> add disabled=no distance=3 dst-address=192.168.0.0/24 gateway=l2tp-vpn +> add disabled=no distance=4 dst-address=192.168.5.0/24 gateway=l2tp-vpn +> add disabled=no distance=1 dst-address=0.0.0.0/0 gateway=WG routing-table=\ +> To-VPN scope=30 target-scope=10 +3969,3970d5736 +< add disabled=no distance=2 dst-address=0.0.0.0/0 gateway=146.120.64.209 \ +< routing-table=main scope=30 target-scope=10 +3974d5739 +< set reverse-proxy disabled=yes +3978c5743 +< set api-ssl address=10.30.30.0/24 disabled=yes +--- +> set api-ssl address=10.30.30.0/24 +3993,3995c5758,5829 +< /snmp community +< set [ find default=yes ] addresses=10.30.30.0/24 read-access=no security=\ +< private +--- +> /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=add-src-to-address-list address-list=bad_ipv6 \ +> address-list-timeout=none-dynamic chain=input comment=\ +> "defconf: accept established,related,untracked" connection-state=\ +> established,related,untracked in-interface=WG in-interface-list=WAN \ +> src-address-list="" +> 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 +> /ppp secret +> add local-address=192.168.60.1 name=vados remote-address=192.168.60.2 \ +> service=l2tp +> /routing bfd configuration +> add disabled=no +> /snmp +> set contact=@AmsterNL enabled=yes engine-id-suffix=MikroMaster location=NL \ +> src-address=10.30.30.81 trap-interfaces=Bridge-LAN,WG trap-target=\ +> 10.30.30.10 +4016,4024d5849 +< add comment="Setup Global Environment for scripts" 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 +4046,4048c5871,5895 +< add comment="Scheduler for Amster-Backup-UpdateEmail" disabled=yes interval=\ +< 1w name=Amster-Backup-UpdateEmail on-event=\ +< "/system/script { run Amster-Backup-UpdateEmail; }" policy=\ +--- +> add comment="Setup Global Environment for scripts" name=AM-SetGlobalEnv \ +> on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ +> policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-time=startup +> add comment="Every Day Check For Update Scripts" interval=1d name=\ +> AM-ScriptInstallUpdate on-event=\ +> ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-time=startup +> add comment="Every Day Check For Fake Bridges" interval=1d name=\ +> mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-time=startup +> add comment="Every Day Check For Renew Certificates" interval=1d name=\ +> AM-CheckCerts on-event="/system/script/run AM-CheckCerts;" policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-time=startup +> add comment="Auto-update Cloudflare Root CA for DoH" interval=2w name=\ +> AM-DOHCert on-event="/import AM-DOHCert.rsc" policy=\ +> ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ +> start-date=2026-04-15 start-time=03:00:00 +> add comment=\ +> "Every 7 Day Check For Update and Backup send notification to EMail." \ +> interval=1w name=AM-Backup-UpdateEmail on-event=\ +> "/system/script { run AM-Backup-UpdateEmail; }" policy=\ +4050c5897 +< start-date=2026-02-13 start-time=03:41:27 +--- +> start-date=2026-04-30 start-time=03:41:27 diff --git a/MikroNUC/20260503/20260503132602-Config.rsc b/MikroNUC/20260503/20260503132602-Config.rsc new file mode 100644 index 0000000..26cdf0b --- /dev/null +++ b/MikroNUC/20260503/20260503132602-Config.rsc @@ -0,0 +1,4786 @@ +# 2026-05-03 13:26:02 by RouterOS 7.22.1 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=Bridge-DOC +add name=Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no name=ether2 +/interface veth +add address=172.18.20.6/30 container-mac-address=00:00:00:00:00:00 dhcp=no \ + gateway=172.18.20.5 gateway6="" name=docker-xray-vless-veth +add address=10.10.10.10/24 container-mac-address=5C:26:FE:05:6F:12 dhcp=no \ + gateway=10.10.10.1 gateway6="" mac-address=5C:26:FE:05:6F:11 name=veth1 +/caps-man datapath +add bridge=Bridge-LAN client-to-client-forwarding=yes local-forwarding=yes \ + name=xs-DataPath +add bridge=Bridge-LAN client-to-client-forwarding=yes local-forwarding=yes \ + name=DataPath +/caps-man security +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=xs-Sec +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=WPA2 +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/disk +add slot=tmpfs tmpfs-max-size=715827882 type=tmpfs +/interface list +add include=dynamic,static name=LAN +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/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=P1505n 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 +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/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=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe;\r\ + \n:global RunOnce;\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs true;\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 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 :local CertSettings [ /certificate/settings/get ];\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 \r\ + \n /tool/fetch (\$GlobalScriptsUrl . \"certs/\" . \$CertFileName) dst-p\ + ath=\$CertFileName as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint \ + ] ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n :delay 1s;\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 :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 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 \"\";\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/\");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass {\r\ + \n \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass {\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/348/e201dbb4\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 04:50:02 01.05.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 138;\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 GlobalEnvRemove;\r\ + \n:global MiniDateTimeStamp;\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 ExitError;\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 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 WaitDNSResolving;\r\ + \n:global WaitFullyConnected;\r\ + \n:global WaitTimeSync;\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 FetchLargeFiles;\r\ + \n:global FetchUserAgentStr;\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 LogPrint;\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n \$LogPrint debug \$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 \$LogPrint debug \$0 (\"Set updates channel to: \$updateChannel\");\ + \r\ + \n }\r\ + \n\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n \$LogPrint debug \$0 (\"Update Channel: \" . \$Channel . \".\");\r\ + \n\r\ + \n\r\ + \n :local StatusPkg \"undefined\";\r\ + \n \$LogPrint debug \$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 \$LogPrint debug \$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 \$LogPrint debug \$0 (\"\\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n \$LogPrint debug \$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\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\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\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\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\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: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: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# !!! The function is not ready !!!\r\ + \n:global FetchHttpInfo do={\r\ + \n :local Mac [ :tostr \$1 ];\r\ + \n :global CertificateAvailable;\r\ + \n :global IsMacLocallyAdministered;\r\ + \n :global LogPrint;\r\ + \n :local Url \"https://api.macvendors.com/\";\r\ + \n :if ([ \$IsMacLocallyAdministered \$Mac ] = true) do={:return \"locall\ + y administered\";}\r\ + \n :do {\r\ + \n :if ([ \$CertificateAvailable \"GTS Root R4\" \"fetch\" ] = false) d\ + o={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Vendor ([ /tool/fetch \r\ + \n (\$Url . [ :pick \$Mac 0 8 ]) output=user as-value ]->\"data\");\r\ + \n :return \$Vendor;\r\ + \n } on-error={\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=yes-without-crl (\$Url) output=none \ + as-value;\r\ + \n \$LogPrint debug \$0 (\"The mac vendor is not known in database.\"\ + );\r\ + \n } do={ \$LogPrint warning \$0 (\"Failed getting mac vendor: \" . \$E\ + rr); }\r\ + \n :return \"unknown vendor\";\r\ + \n }\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 :global CertificateDownload;\r\ + \n :global EitherOr;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :set UseFor [ \$EitherOr \$UseFor \"undefined\" ];\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 \$LogPrint 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 \$LogPrint 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 \$LogPrint info \$0 (\"Certificate with CommonName '\" . \$CommonNam\ + e . \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={:return fal\ + se}\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n \$LogPrint 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 \$LogPrint info \$0 (\"Certificate chain for '\" . \$CommonName . \ + \"' is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={:return fal\ + se}\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 GlobalScriptsUrl;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global LogPrint;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n \r\ + \n \$LogPrint info \$0 (\"Downloading and importing certificate with Comm\ + onName '\" . \$CommonName . \"'.\");\r\ + \n \r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl http-header-field=({ [\ + \_\$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$GlobalScriptsUrl . \"certs/\" . \$FileName . \$GlobalScriptsUrl\ + Suffix) dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n \$LogPrint warning \$0 (\"Failed downloading certificate with Common\ + Name '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"Required certificate is not available.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl http-header-field=({\ + \_[ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n \"https://mkcert.org/generate/\" http-data=[ :serialize to=json \ + ({ \$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 \$LogPrint warning \$0 (\"Failed downloading certificate with Comm\ + onName '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n \$LogPrint warning \$0 (\"Certificate with CommonName '\" . \$Common\ + Name . \"' 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 :global LogPrint;\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 \$LogPrint 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 FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\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 (https://amster.vados.ru/; main/3419/3ee\ + 19780)\");\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 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 GetRandomNumber do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndnum from=0 to=[ \$EitherOr [ :tonum \$1 ] 4294967295 ] ]\ + \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 MiniDateTimeStamp do={\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 :return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\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={:return false}\r\ + \n :local FileVal false;\r\ + \n :do {: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 :global LogPrint;\r\ + \n \$LogPrint debug \$0 (\"Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\$LogPrint debug \$0 (\"... which does not \ + exist.\"); :return true};\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\$LogPrint error \$0 (\"\ + Directory '\" . \$DirName . \"' is not a directory.\"); :return false};\r\ + \n :onerror Err {/file/remove \$DirName} do={\$LogPrint error \$0 (\"Remo\ + ving directory '\" . \$DirName . \"' failed: \" . \$Err); :return false};\ + \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 :global LogPrint;\r\ + \n \$LogPrint debug \$0 (\"Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\$LogPrint debug \$0 (\"... which does not\ + \_exist.\"); :return true}\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n \$LogPrint error \$0 (\"File '\" . \$FileName . \"' is not a file.\"\ + ); :return false}\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\$LogPrint error \$0 (\"Removing file '\" . \$FileName . \"' fai\ + led: \" . \$Err); :return false}\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 :global LogPrint;\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 \$LogPrint info \$0 (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n \$LogPrint 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 \$LogPrint 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 \$LogPrint debug \$0 (\"Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n \$LogPrint 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 \$LogPrint 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 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 [ :len\ + \_\$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 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 LogPrint;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n \$LogPrint 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 \$LogPrint warning \$0 (\"This \" . [ \$IfThenElse ([ :pick \$Call\ + er 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: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: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 LogPrint;\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 \$LogPrint error \$0 (\"A script named '\" . \$Script . \"' does not\ + \_exist!\");\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 \$LogPrint error \$0 (\"More tickets than running scripts '\" . \$Sc\ + ript . \"', 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: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: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:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global LogPrint;\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 \$LogPrint 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: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: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: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:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\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: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# read parts of a large file\r\ + \n:set FetchLargeFiles do={\r\ + \n :local ScriptName [:tostr \$1];\r\ + \n :local fetchUrl [:tostr \$2];\r\ + \n :local CheckCert [:tostr \$3];\r\ + \n :local fetchMode [:tostr \$4];\r\ + \n :local fetchMethod [:tostr \$5];\r\ + \n :local FetchString \"undefined\";\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 #:set fetchMode [ \$IfThenElse (\$fetchMode != \"false\") [:tostr (\"m\ + ode=\" . \$fetchMode)] ];\r\ + \n #:set fetchMethod [\$IfThenElse (\$fetchMethod != \"false\") [:tostr (\ + \"http-method=\\\"\" . \$fetchMethod . \"\\\"\") ]];\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 :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n #/tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName h\ + ttp-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-value;\r\ + \n #:log warning (\"\\\$fUrl =\" . \$2 . ({[ \$fMode ]}));\r\ + \n :onerror Err {\r\ + \n :set FetchString (\"/tool/fetch \" . \\\r\ + \n [\$IfThenElse ([\$fetchMethod] != \"false\") (\"http-method=\\\"\"\ + \_. \$fetchMethod . \"\\\" \")] . \\\r\ + \n \"check-certificate=\" . \$CheckCert . \" url=(\\\"\" . \$fetchUrl\ + \_. \"\\\") \" . \\\r\ + \n [\$IfThenElse ([\$fetchMode] != \"false\") (\"mode=\" . \$fetchMod\ + e . \" \")] . \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue);\r\ + \n :log warning (\"\\\$FetchSring = \" . \$FetchString);\r\ + \n :put [\$FetchString];\r\ + \n #:put [/tool/fetch \$fetchMode check-certificate=\$CheckCert url=(\$\ + fetchUrl) dst-path=(\$FileName) http-header-field=({ [ \$FetchUserAgentStr\ + \_\$ScriptName ] }) as-value];\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 \" . \$fetchUrl . \"\ + \_- \" . \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$fetchUrl . \ + \" did not show up.\");\r\ + \n :return false;\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 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={:set NotificationMessage (\$NotificationMessage . \"\\n\\nNe\ + ws and changes are not available.\")}\r\ + \n\r\ + \n :set GlobalConfChanges;\r\ + \n :set GlobalConfMigration;\r\ + \n }\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n# Global Environment clear. Use: \"\$GlobalEnvRemove True;\".\r\ + \n:set GlobalEnvRemove do={\r\ + \n :local doRmEnv [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ScriptsUrl;\r\ + \n :global LogPrint;\r\ + \n :if (\$doRmEnv != \"True\") do={\r\ + \n \$LogPrint error \"GlobalEnvRemove\" (\"This script is needed \\\"T\ + rue\\\" for confirm to remove global environment variables!\");\r\ + \n :return [];\r\ + \n :error true; \r\ + \n } else={\r\ + \n :set ScriptsUrl \$GlobalScriptsUrl;\r\ + \n :delay 5;\r\ + \n :foreach var in=[/system/script/environment print as-value] do={/sy\ + stem/script/environment remove (\$var->\".id\")}\r\ + \n :delay 5;\r\ + \n :global GlobalScriptsUrl \$ScriptsUrl;\r\ + \n \$LogPrint warning \"GlobalEnvRemove\" (\"Done!\");\r\ + \n :return [];\r\ + \n :error false;\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 \"Setup Global Environment for scripts\";\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#:local Resource [ /system/resource/get ];\r\ + \n#:log info (\$ScriptName . \" Loaded on \" . \$Resource->\"board-name\" \ + . \" with RouterOS \" . \$Resource->\"version\" . \" and Architecture \" .\ + \".\");\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# signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\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} do={:global ExitOnError; \$ExitOnError \$0 \$Err;}\r\ + \n\r\ + \n#} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n# log the successful load of the script, or an error if it failed\r\ + \n#:if ([\$GlobalFuncReady] = true && [\$GlobalConfReady] = true) do={\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\r\ + \n#\r\ + \n#root.mnuc.backuper\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n#:local FetchFile AM-DOHCert.rsc\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n:global MkDir;\r\ + \n\r\ + \n:local FetchFile Binary.backup\r\ + \n:local localDir \"[usb1]/scripts/\";\r\ + \n\r\ + \n\$MkDir \$localDir;\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local FwAddrListsUrl (\$GlobalScriptsUrl . \"fw-addr-lists.d/\");\r\ + \n\r\ + \n :local FwAddrLists {\r\ + \n \"allow\"={\r\ + \n { url=(\$FwAddrListsUrl . \"allow\");\r\ + \n cert=\"ISRG Root X2\"; timeout=1w };\r\ + \n };\r\ + \n \"block\"={\r\ + \n { url=(\$FwAddrListsUrl . \"block\");\r\ + \n cert=\"ISRG Root X2\" };\r\ + \n# { url=\"https://raw.githubusercontent.com/stamparm/ipsum/refs/hea\ + ds/master/levels/4.txt\";\r\ + \n# # higher level (decrease the numerical value) for more addresses, \ + and vice versa\r\ + \n# cert=\"ISRG Root X1\" };\r\ + \n# { url=\"https://www.dshield.org/block.txt\"; cidr=\"/24\";\r\ + \n# cert=\"GTS Root R4\" };\r\ + \n# { url=\"https://lists.blocklist.de/lists/strongips.txt\";\r\ + \n# cert=\"GTS Root R4\" };\r\ + \n# { url=\"https://www.spamhaus.org/drop/drop_v4.json\";\r\ + \n# cert=\"GTS Root R4\" };\r\ + \n# { url=\"https://www.spamhaus.org/drop/drop_v6.json\";\r\ + \n# cert=\"GTS Root R4\" };\r\ + \n };\r\ + \n# \"mikrotik\"={\r\ + \n# { url=(\$FwAddrListsUrl . \"mikrotik\");\r\ + \n# cert=\"ISRG Root X2\"; timeout=1w };\r\ + \n# };\r\ + \n \"FuckRKN\"={\r\ + \n { url=(\$FwAddrListsUrl . \"FuckRKN\");\r\ + \n cert=\"ISRG Root X2\"; timeout=1w };\r\ + \n };\r\ + \n };\r\ + \n :local FwAddrListTimeOut 1d;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHuge;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len [ /log/find where topics=({\"script\"; \"warning\"}) \\\r\ + \n message=(\"\\\$LogPrint: The message is already in log, scripting \ + subsystem may have crashed before!\") ] ] > 0) do={\r\ + \n :log warning (\$ScriptName . \" Scripting subsystem may have crashed\ + , possibly caused by us. Delaying!\");\r\ + \n :delay 5m;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n\r\ + \n :foreach FwListName,FwList in=\$FwAddrLists do={\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n\r\ + \n :foreach List in=\$FwList do={\r\ + \n :local CheckCertificate false;\r\ + \n :local Data false;\r\ + \n :local TimeOut [ \$EitherOr [ :totime (\$List->\"timeout\") ] \$Fw\ + AddrListTimeOut ];\r\ + \n\r\ + \n :if ([ :len (\$List->\"cert\") ] > 0) do={\r\ + \n :set CheckCertificate true;\r\ + \n :if ([ \$CertificateAvailable (\$List->\"cert\") \"fetch\" ] = f\ + alse) do={\r\ + \n :log warning (\$ScriptName . \" Downloading required certifica\ + te (\" . \$FwListName . \\\r\ + \n \" / \" . \$List->\"url\" . \") failed, trying anyway.\");\ + \r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHuge \$ScriptName (\$List->\"url\")\ + \_\$CheckCertificate ] ];\r\ + \n :if (\$Data = false) do={\r\ + \n :if (\$I < 5) do={\r\ + \n :log debug (\$ScriptName . \" Failed downloading for list \ + '\" . \$FwListName . \\\r\ + \n \"', \" . \$I . \". try from: \" . \$List->\"url\");\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" \ + . \$FwListName . \\\r\ + \n \"' from: \" . \$List->\"url\");\r\ + \n } else={\r\ + \n :log debug (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \\\r\ + \n \"B for list '\" . \$FwListName . \"' from: \" . \$List->\"u\ + rl\");\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Li\ + ne ] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"\ + cidr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\ + \\.[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address\ + \_] ];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$C\ + IDR ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3\ + })\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address\ + \_] ];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \ + \"/\" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\")\ + \_do={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :error true;\r\ + \n }\r\ + \n } on-error={:log error (\$ScriptName . \" Failed parsing address\ + : \" . \$Line->0);}\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$FwListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log debug (\$ScriptName . \" Renewing IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" in list '\" . \$FwListName . \"' with \" . \$TimeOut . \"\ + .\");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log debug (\$ScriptName . \" Removing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" from list '\" . \$FwListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$FwListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log debug (\$ScriptName . \" Renewing IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" in list '\" . \$FwListName . \"' with \" . \$TimeOut . \"\ + .\");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log debug (\$ScriptName . \" Removing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" from list '\" . \$FwListName .\".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log debug (\$ScriptName . \" Handling branch: \" . \$BranchName);\ + \r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log debug (\$ScriptName . \" Adding IPv4 address \" . \$Address\ + \_. \\\r\ + \n \" to list '\" . \$FwListName . \"' with \" . \$Timeout . \"\ + .\");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$FwListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$FwListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log debug (\$ScriptName . \" Handling branch: \" . \$BranchName);\ + \r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log debug (\$ScriptName . \" Adding IPv6 address \" . \$Address\ + \_. \\\r\ + \n \" to list '\" . \$FwListName . \"' with \" . \$Timeout . \"\ + .\");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$FwListName comment=\$Li\ + stComment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$FwListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$FwListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +/app settings +set auto-update=yes disk=sata1 lan-bridge=Bridge-LAN registry-mirrors="https:/\ + /reg.vados.ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man manager +# bad package path +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes package-path=/tmpfs/capsman-packages +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set builtin-trust-store=fetch crl-download=yes crl-use=yes +/container config +set layer-dir=/sata1 registry-url=https://reg.vados.ru tmpdir=/ramstorage \ + username=amster-dev +/container envs +add key=ENV list=carkeeperbot_envs value=/root/.ashrc +add key=PATH list=carkeeperbot_envs value=\ + /usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin +add key=PORT list=carkeeperbot_envs value="\${SSH_PORT}" +add key=SSH_PORT list=carkeeperbot_envs value=8222 +/container mounts +add dst=/app list=carkeeper_app src=/sata1/carkeeper/app +add dst=/configs list=carkeeper_configs src=/sata1/carkeeper/configs +add dst=/root/.ashrc list=carkeeper_configs/.profile src=\ + /sata1/carkeeper/configs/.profile +add dst=/etc/php82/php.ini list=carkeeper_configs/php.ini src=\ + /sata1/carkeeper/configs/php.ini +add dst=/etc/ssh/sshd_config list=carkeeper_configs/sshd_config src=\ + /sata1/carkeeper/configs/sshd_config +add dst=/scripts list=carkeeper_scripts src=/sata1/carkeeper/scripts +add dst=/var/sync list=carkeeper_sync src=/sata1/carkeeper/sync +add dst=/var/tmp list=carkeeper_tmp src=/sata1/carkeeper/tmp +/interface bridge port +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-DOC interface=veth1 +add bridge=Bridge-LAN interface=LAN +add bridge=Bridge-LAN interface=dynamic +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=LAN internet-interface-list=LAN lan-interface-list=\ + LAN wan-interface-list=LAN +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/ip address +add address=10.30.30.10/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.10.10.1/24 disabled=yes interface=Bridge-DOC network=\ + 10.10.10.0 +add address=172.18.20.5/30 disabled=yes interface=docker-xray-vless-veth \ + network=172.18.20.4 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=Bridge-LAN \ + lease-script=lease-script lease-time=10m name=nucDHCP server-address=\ + 10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.81 verify-doh-cert=yes +/ip firewall filter +add action=accept chain=input +add action=accept chain=output +add action=accept chain=forward comment="Comment of the rule" disabled=yes +/ip firewall mangle +# docker-xray-vless-veth not ready +add action=change-mss chain=forward new-mss=1360 out-interface=\ + docker-xray-vless-veth protocol=tcp tcp-flags=syn tcp-mss=1420-65535 +/ip firewall nat +# docker-xray-vless-veth not ready +add action=masquerade chain=srcnat out-interface=docker-xray-vless-veth +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add disabled=no dst-address=0.0.0.0/0 gateway=10.30.30.81 routing-table=main +/ip service +set ftp disabled=yes +set www disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api disabled=yes +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both password-authentication=no \ + publickey-authentication-options=verify-required strong-crypto=yes +/ip upnp +set allow-disable-external-interface=yes enabled=yes +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Setup Global Environment for scripts" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" interval=30s name=mod/AM-TG-Bot on-event=\ + "/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-04-30 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +/user aaa +set accounting=no -- 2.52.0