From 3c8d8a5d8beb75d0899faccaf719e36f5f33cfd7 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 28 Jun 2026 05:00:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 28 05:00:03. --- .../20260628/20260628044003-Config.rsc | 3691 +++++++++++++ MikroMaster/tmp/Config.rsc | 0 MikroMaster/tmp/Diff-Config.rsc | 163 + .../20260628/20260628050001-Binary.backup | Bin 0 -> 361838 bytes MikroNUC/20260628/20260628050001-Config.rsc | 4903 +++++++++++++++++ .../20260628/20260628050001-Diff-Config.rsc | 220 + 6 files changed, 8977 insertions(+) create mode 100644 MikroMaster/20260628/20260628044003-Config.rsc delete mode 100644 MikroMaster/tmp/Config.rsc create mode 100644 MikroMaster/tmp/Diff-Config.rsc create mode 100644 MikroNUC/20260628/20260628050001-Binary.backup create mode 100644 MikroNUC/20260628/20260628050001-Config.rsc create mode 100644 MikroNUC/history/20260628/20260628050001-Diff-Config.rsc diff --git a/MikroMaster/20260628/20260628044003-Config.rsc b/MikroMaster/20260628/20260628044003-Config.rsc new file mode 100644 index 0000000..f73f5c0 --- /dev/null +++ b/MikroMaster/20260628/20260628044003-Config.rsc @@ -0,0 +1,3691 @@ +# 2026-06-28 04:40:07 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +set [ find default-name=wlan1 ] country=belarus mode=ap-bridge ssid=vados \ + wireless-protocol=802.11 +# managed by CAPsMAN +set [ find default-name=wlan2 ] country=belarus mode=ap-bridge ssid=vados5g \ + wireless-protocol=802.11 +/interface wireguard +add comment=WG disabled=yes listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +add disabled=no fib name=To-AWG +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add disabled=yes interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 disabled=yes \ + endpoint-address=109.107.177.79 endpoint-port=29185 interface=WG name=\ + VDSina persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 disabled=yes interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=4 \ + default-route-tables=main:4 dhcp-options=clientid_duid,clientid \ + interface="ether1 - Unet" name=client1 use-broadcast=never use-peer-dns=\ + no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81,10.30.30.33 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=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=accept chain=input disabled=yes dst-port=18880 in-interface=\ + Bridge-LAN protocol=tcp src-address=10.30.30.0/24 +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + disabled=yes new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" disabled=yes dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" disabled=yes dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=To-AWG routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=To-VPN routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" disabled=yes \ + new-connection-mark=VPN_No_FastTrack routing-mark=To-AWG +add action=change-mss chain=forward comment="MSS on WG" disabled=yes new-mss=\ + 1380 out-interface=*16 protocol=tcp routing-mark=To-AWG tcp-flags=syn \ + tcp-mss=1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" disabled=yes \ + dst-address=10.8.0.0/24 new-routing-mark=To-AWG src-address=10.30.30.0/24 +/ip firewall nat +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG disabled=yes dst-address-list=\ + !NoVdsina out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=yes distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/tmp/Config.rsc b/MikroMaster/tmp/Config.rsc deleted file mode 100644 index e69de29..0000000 diff --git a/MikroMaster/tmp/Diff-Config.rsc b/MikroMaster/tmp/Diff-Config.rsc new file mode 100644 index 0000000..a81bca7 --- /dev/null +++ b/MikroMaster/tmp/Diff-Config.rsc @@ -0,0 +1,163 @@ +22,23c22,24 +< set [ find default-name=wlan1 ] country=belarus mode=ap-bridge ssid=vados \ +< wireless-protocol=802.11 +--- +> # channel: 2442/20-Ce/gn(18dBm), SSID: vados, local forwarding +> set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ +> ssid=vados wireless-protocol=802.11 +25,26c26,28 +< set [ find default-name=wlan2 ] country=belarus mode=ap-bridge ssid=vados5g \ +< wireless-protocol=802.11 +--- +> # channel: 5260/20-Ceee/ac/DP(18dBm), SSID: vados5g, local forwarding +> set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ +> ssid=vados5g wireless-protocol=802.11 +28c30 +< add comment=WG disabled=yes listen-port=13231 mtu=1320 name=WG +--- +> add comment=WG listen-port=13231 mtu=1320 name=WG +36,37d37 +< /iot wiliot servers +< set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +92d91 +< add disabled=no fib name=To-AWG +2578a2578 +> # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +2580a2581 +> # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +2582a2584 +> # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +2584a2587 +> # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +2587a2591 +> # ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +2591c2595 +< set detect-interface-list=WAN internet-interface-list=Internet \ +--- +> set detect-interface-list=static internet-interface-list=Internet \ +2596,2598d2599 +< add interface=ether4 list=WAN +< add interface="ether1 - Unet" list=Internet +< add disabled=yes interface=WG list=WAN +2599a2601,2602 +> add interface="ether1 - Unet" list=Internet +> add interface=WG list=WAN +2601,2603c2604,2606 +< add allowed-address=0.0.0.0/0 client-allowed-address=::/0 disabled=yes \ +< endpoint-address=109.107.177.79 endpoint-port=29185 interface=WG name=\ +< VDSina persistent-keepalive=10s public-key=\ +--- +> add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ +> 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ +> persistent-keepalive=10s public-key=\ +2613c2616 +< add address=10.8.0.2/24 disabled=yes interface=WG network=10.8.0.0 +--- +> add address=10.8.0.2/24 interface=WG network=10.8.0.0 +2617,2620c2620,2622 +< add add-default-route=special-classless default-route-distance=4 \ +< default-route-tables=main:4 dhcp-options=clientid_duid,clientid \ +< interface="ether1 - Unet" name=client1 use-broadcast=never use-peer-dns=\ +< no use-peer-ntp=no +--- +> add add-default-route=special-classless default-route-distance=2 \ +> default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ +> interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +2677,2678c2679 +< doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ +< verify-doh-cert=yes +--- +> doh-max-server-connections=20 servers=8.8.4.4,8.8.8.8 verify-doh-cert=yes +3227c3228 +< add address=10.30.30.33 comment=amster list=NoWG +--- +> add address=10.30.30.33 comment=amster disabled=yes list=NoWG +3267d3267 +< add address=10.30.30.10 list=AWG-Proxy +3292c3292 +< connection-state=established,related,new +--- +> connection-state=established,related,new disabled=yes +3336,3337d3335 +< add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ +< disabled=yes new-routing-mark=main src-address-list=NoWG +3339c3337 +< "Mark Routing main for NoVdsina" disabled=yes dst-address-list=NoVdsina \ +--- +> "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ +3342,3345c3340 +< "Mark Routing main for VDSina" disabled=yes dst-address-list=VPN-Server \ +< new-routing-mark=main src-address=10.30.30.0/24 +< add action=mark-routing chain=prerouting comment=\ +< "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ +--- +> "Mark Routing main for VDSina" dst-address-list=VPN-Server \ +3356,3359c3351,3352 +< add action=mark-routing chain=prerouting comment="AWG for All sites" \ +< disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ +< new-routing-mark=To-AWG routing-mark=!main src-address=10.30.30.0/24 \ +< src-address-list=!NoWG +--- +> add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ +> disabled=yes new-routing-mark=main src-address-list=NoWG +3361,3363c3354,3355 +< disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ +< new-routing-mark=To-VPN routing-mark=!main src-address=10.30.30.0/24 \ +< src-address-list=!NoWG +--- +> dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ +> To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +3365,3371c3357,3363 +< "Mark VPN connections to exclude from FastTrack" disabled=yes \ +< new-connection-mark=VPN_No_FastTrack routing-mark=To-AWG +< add action=change-mss chain=forward comment="MSS on WG" disabled=yes new-mss=\ +< 1380 out-interface=*16 protocol=tcp routing-mark=To-AWG tcp-flags=syn \ +< tcp-mss=1381-65535 +< add action=mark-routing chain=prerouting comment="VPN Network" disabled=yes \ +< dst-address=10.8.0.0/24 new-routing-mark=To-AWG src-address=10.30.30.0/24 +--- +> "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ +> VPN_No_FastTrack routing-mark=To-VPN +> add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ +> out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ +> 1381-65535 +> add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ +> 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +3373,3378d3364 +< add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ +< fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ +< to-addresses=10.30.30.33 to-ports=443 +< add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ +< dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ +< 10.30.30.33 to-ports=80 +3396a3383,3388 +> add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-address=\ +> 146.120.64.211 dst-port=443 fragment=no in-interface="ether1 - Unet" \ +> protocol=tcp routing-mark=main to-addresses=10.30.30.33 to-ports=443 +> add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ +> dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ +> 10.30.30.33 to-ports=80 +3418,3419c3410,3411 +< add action=masquerade chain=srcnat comment=WG disabled=yes dst-address-list=\ +< !NoVdsina out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +--- +> add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ +> out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +3506c3498 +< add check-gateway=none disabled=yes distance=1 dst-address=0.0.0.0/0 gateway=\ +--- +> add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ +3517c3509 +< set www address=10.30.30.0/24 disabled=yes port=18880 +--- +> set www address=10.30.30.0/24 port=18880 +3520,3521c3512,3513 +< no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ +< webfig-plain=no webfig-secure=no +--- +> no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ +> webfig-secure=no +3632c3624 +< set check-certificate=yes-without-crl ip-version=ipv4 +--- +> set check-certificate=yes-without-crl diff --git a/MikroNUC/20260628/20260628050001-Binary.backup b/MikroNUC/20260628/20260628050001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..acf1bc8762e31b33d8790bc35a49cacbe2960416 GIT binary patch literal 361838 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 literal 0 HcmV?d00001 diff --git a/MikroNUC/20260628/20260628050001-Config.rsc b/MikroNUC/20260628/20260628050001-Config.rsc new file mode 100644 index 0000000..1331df3 --- /dev/null +++ b/MikroNUC/20260628/20260628050001-Config.rsc @@ -0,0 +1,4903 @@ +# 2026-06-28 05:00:02 by RouterOS 7.23.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-LAN +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no name=ether2 +/interface veth +add address=172.18.9.106/30 container-mac-address=00:00:00:00:00:00 dhcp=no \ + gateway=172.18.9.105 gateway6="" name=veth-awg-proxy +/interface wireguard +add disabled=yes listen-port=13241 mtu=1420 name=wg-awg-proxy +/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 +/disk +add slot=ram tmpfs-max-size=100000000 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=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 +/routing table +add disabled=no fib name=To-AWG +/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 \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/700/f14f095d\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:03 22.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\r\ + \n#\r\ + \n#root.mnuc.backuper\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n#:local Script AM-GlobalConfig;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n#/usb1/mikro_bak_rsa.pub\r\ + \n#:local FetchFile AM-DOHCert.rsc\r\ + \n\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\r\ + \n:global MkDir;\r\ + \n\r\ + \n:local FetchFile Binary.backup\r\ + \n:local localDir \"[usb1]/scripts/\";\r\ + \n\r\ + \n\$MkDir \$localDir;\r\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add comment=awg-proxy dont-require-permissions=no name=awg-proxy-uninstall \ + owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-proxy-e\ + nv\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-prox\ + y-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n /ip/route/remove [find where comment=awg-proxy-tunnel]\ + \n /ip/route/remove [find where comment=awg-proxy-dns]\ + \n /container/stop [find where interface=veth-awg-proxy]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-proxy]\ + \n /container/envs/remove [find where list=\"awg-proxy-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-proxy\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.18.9.104/30\"]\ + \n /ip/address/remove [find where address=\"172.18.9.105/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-proxy\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-proxy\ + \"]\ + \n /ip/address/remove [find where interface=\"wg-awg-proxy\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-proxy\"]\ + \n :do { /file/remove [find where name~\"awg-proxy.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=\"sata1/awg-proxy\"] } on-error={}\ + \n :do { /file/remove [find where name=\"sata1/ru_ranges_timeout.rsc\"] }\ + \_on-error={}\ + \n :do { /file/remove [find where name~\"sata1/pull\"] } on-error={}\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-proxy-uninstall]\ + \n" +/user group +add name=backup policy="telnet,ssh,ftp,read,write,policy,test,sniff,sensitive,\ + !local,!reboot,!winbox,!password,!web,!api,!romon,!rest-api" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 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 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 +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=Bridge-LAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1 memory-high=200.0MiB memory-max=4096.0MiB registry-url=\ + https://registry-1.docker.io tmpdir=/sata1/pull username=amsternl +/container envs +add key=AWG_CLIENT_PUB list=awg-proxy-env value=\ + "NAonhRSuFIrFpNKTs5X32ItcvFuO1kiJUigQZb95YlE=" +add key=AWG_H1 list=awg-proxy-env value=472437748-472438637 +add key=AWG_H2 list=awg-proxy-env value=707808381-707809250 +add key=AWG_H3 list=awg-proxy-env value=1609078552-1609079495 +add key=AWG_H4 list=awg-proxy-env value=1012329484-1012329741 +add key=AWG_I1 list=awg-proxy-env value=\ + "" +add key=AWG_I2 list=awg-proxy-env value="" +add key=AWG_I3 list=awg-proxy-env value="" +add key=AWG_I4 list=awg-proxy-env value="" +add key=AWG_I5 list=awg-proxy-env value="" +add key=AWG_JC list=awg-proxy-env value=9 +add key=AWG_JMAX list=awg-proxy-env value=920 +add key=AWG_JMIN list=awg-proxy-env value=105 +add key=AWG_LISTEN list=awg-proxy-env value=:51820 +add key=AWG_PREV_DNS list=awg-proxy-env value=10.30.30.33 +add key=AWG_PREV_TMPDIR list=awg-proxy-env value="" +add key=AWG_REMOTE list=awg-proxy-env value=109.107.177.79:55055 +add key=AWG_S1 list=awg-proxy-env value=90 +add key=AWG_S2 list=awg-proxy-env value=136 +add key=AWG_S3 list=awg-proxy-env value=45 +add key=AWG_S4 list=awg-proxy-env value=27 +add key=AWG_SERVER_PUB list=awg-proxy-env value=\ + "o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/interface bridge port +add bridge=Bridge-LAN interface=ether2 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes \ + forward=no +/interface detect-internet +set detect-interface-list=WAN internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=lo list=LAN +add interface=*D9 list=Internet +add interface=ether2 list=WAN +add interface=*3 list=LAN +/interface wifi cap +set discovery-interfaces=Bridge-LAN +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/interface wireguard peers +add allowed-address=0.0.0.0/0 endpoint-address=172.18.9.106 endpoint-port=\ + 51820 interface=wg-awg-proxy name=peer14 persistent-keepalive=10s \ + public-key="o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +/ip address +add address=10.30.30.10/24 interface=ether2 network=10.30.30.0 +add address=172.18.9.105/30 interface=veth-awg-proxy network=172.18.9.104 +add address=10.9.0.3 interface=wg-awg-proxy network=10.9.0.3 +/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=*3 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.9.0.1 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 name=vds.vados.ru type=A +/ip firewall nat +add action=masquerade chain=srcnat src-address=172.18.9.104/30 +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + 10.30.30.81 routing-table=main scope=30 target-scope=10 +add comment=awg-proxy-dns distance=1 dst-address=10.9.0.1/32 gateway=\ + wg-awg-proxy +/ip service +set ftp disabled=yes +set www disabled=yes +set ssh port=2222 +set www-ssl port=4443 +set api disabled=yes +set api-ssl disabled=yes +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-plain=no rest-secure=no scep-plain=no \ + webfig-plain=no webfig-secure=no +/ip ssh +set forwarding-enabled=both password-authentication=no \ + publickey-authentication-options=verify-required strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes 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" disabled=yes 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" disabled=yes 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" disabled=yes 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." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool netwatch +add disabled=yes down-script=\ + ":local msg \"Host MikroYellow is DOWN\";\r\ + \n\$TGSendMessage \$msg;" host=10.30.30.86 http-codes="" interval=10s \ + test-script="" type=simple up-script=\ + ":global TGSendMessage;\r\ + \n\$TGSendMessage \"Host MikroYellow is UP\";" +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260628/20260628050001-Diff-Config.rsc b/MikroNUC/history/20260628/20260628050001-Diff-Config.rsc new file mode 100644 index 0000000..3639804 --- /dev/null +++ b/MikroNUC/history/20260628/20260628050001-Diff-Config.rsc @@ -0,0 +1,220 @@ +23,24d22 +< /interface bridge +< add name=Bridge-LAN +28,29c26,28 +< add address=172.18.9.106/30 container-mac-address=00:00:00:00:00:00 dhcp=no \ +< gateway=172.18.9.105 gateway6="" name=veth-awg-proxy +--- +> add address=172.28.232.14/30 container-mac-address=22:A6:30:44:4A:A0 dhcp=no \ +> gateway=172.28.232.13 gateway6="" mac-address=22:A6:30:44:4A:9F name=\ +> veth-awg-MikroNuc +31c30 +< add disabled=yes listen-port=13241 mtu=1420 name=wg-awg-proxy +--- +> add listen-port=14587 mtu=1280 name=wg-awg-MikroNuc +36a36,40 +> /container +> add comment=awg-MikroNuc envlists=awg-MikroNuc-env file=\ +> sata1/pull/awg-proxy-amd64.tar.gz hostname=awg-MikroNuc interface=\ +> veth-awg-MikroNuc layer-dir=/sata1 name=awg-proxy-amd64 root-dir=\ +> /sata1/awg-MikroNuc shm-size=4096.0KiB start-on-boot=yes +4637,4638c4641,4642 +< add comment=awg-proxy dont-require-permissions=no name=awg-proxy-uninstall \ +< owner=vados policy=\ +--- +> add comment=awg-MikroNuc dont-require-permissions=no name=\ +> awg-MikroNuc-uninstall owner=vados policy=\ +4643,4644c4647,4648 +< \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-proxy-e\ +< nv\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ +--- +> \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-MikroNu\ +> c-env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ +4646,4650c4650,4654 +< \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-prox\ +< y-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ +< \n /ip/route/remove [find where comment=awg-proxy-tunnel]\ +< \n /ip/route/remove [find where comment=awg-proxy-dns]\ +< \n /container/stop [find where interface=veth-awg-proxy]\ +--- +> \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-Mikr\ +> oNuc-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ +> \n /ip/route/remove [find where comment=awg-MikroNuc-tunnel]\ +> \n /ip/route/remove [find where comment=awg-MikroNuc-dns]\ +> \n /container/stop [find where interface=veth-awg-MikroNuc]\ +4652,4653c4656,4657 +< \n /container/remove [find where interface=veth-awg-proxy]\ +< \n /container/envs/remove [find where list=\"awg-proxy-env\"]\ +--- +> \n /container/remove [find where interface=veth-awg-MikroNuc]\ +> \n /container/envs/remove [find where list=\"awg-MikroNuc-env\"]\ +4660,4669c4664,4675 +< \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-proxy\"]\ +< \n /ip/firewall/nat/remove [find where src-address=\"172.18.9.104/30\"]\ +< \n /ip/address/remove [find where address=\"172.18.9.105/30\"]\ +< \n /interface/veth/remove [find where name=\"veth-awg-proxy\"]\ +< \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-proxy\ +< \"]\ +< \n /ip/address/remove [find where interface=\"wg-awg-proxy\"]\ +< \n /interface/wireguard/remove [find where name=\"wg-awg-proxy\"]\ +< \n :do { /file/remove [find where name~\"awg-proxy.+tar\"] } on-error={}\ +< \n :do { /file/remove [find where name=\"sata1/awg-proxy\"] } on-error={}\ +--- +> \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-MikroNuc\"]\ +> \n /ip/firewall/nat/remove [find where src-address=\"172.28.232.12/30\"]\ +> \n /ip/address/remove [find where address=\"172.28.232.13/30\"]\ +> \n /interface/veth/remove [find where name=\"veth-awg-MikroNuc\"]\ +> \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-Mikro\ +> Nuc\"]\ +> \n /ip/address/remove [find where interface=\"wg-awg-MikroNuc\"]\ +> \n /interface/wireguard/remove [find where name=\"wg-awg-MikroNuc\"]\ +> \n :do { /file/remove [find where name~\"awg-MikroNuc.+tar\"] } on-error=\ +> {}\ +> \n :do { /file/remove [find where name=\"sata1/awg-MikroNuc\"] } on-error\ +> ={}\ +4675c4681 +< \n /system/script/remove [find where name=awg-proxy-uninstall]\ +--- +> \n /system/script/remove [find where name=awg-MikroNuc-uninstall]\ +4697,4698c4703,4704 +< set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ +< ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +--- +> set auto-update=yes disk=*7 lan-bridge=Bridge-LAN registry-mirrors="https://re\ +> g.vados.ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +4725,4728c4731,4737 +< add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ +< xs-DataPath +< add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ +< 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 +> /interface bridge +> add admin-mac=F0:75:5D:1E:5F:85 auto-mac=no mlag-peer-port=ether2 name=\ +> Bridge-LAN port-cost-mode=short +4750c4759 +< add key=AWG_CLIENT_PUB list=awg-proxy-env value=\ +--- +> add key=AWG_CLIENT_PUB list=awg-MikroNuc-env value=\ +4752,4756c4761,4765 +< add key=AWG_H1 list=awg-proxy-env value=472437748-472438637 +< add key=AWG_H2 list=awg-proxy-env value=707808381-707809250 +< add key=AWG_H3 list=awg-proxy-env value=1609078552-1609079495 +< add key=AWG_H4 list=awg-proxy-env value=1012329484-1012329741 +< add key=AWG_I1 list=awg-proxy-env value=\ +--- +> add key=AWG_H1 list=awg-MikroNuc-env value=472437748-472438637 +> add key=AWG_H2 list=awg-MikroNuc-env value=707808381-707809250 +> add key=AWG_H3 list=awg-MikroNuc-env value=1609078552-1609079495 +> add key=AWG_H4 list=awg-MikroNuc-env value=1012329484-1012329741 +> add key=AWG_I1 list=awg-MikroNuc-env value=\ +4758,4773c4767,4782 +< add key=AWG_I2 list=awg-proxy-env value="" +< add key=AWG_I3 list=awg-proxy-env value="" +< add key=AWG_I4 list=awg-proxy-env value="" +< add key=AWG_I5 list=awg-proxy-env value="" +< add key=AWG_JC list=awg-proxy-env value=9 +< add key=AWG_JMAX list=awg-proxy-env value=920 +< add key=AWG_JMIN list=awg-proxy-env value=105 +< add key=AWG_LISTEN list=awg-proxy-env value=:51820 +< add key=AWG_PREV_DNS list=awg-proxy-env value=10.30.30.33 +< add key=AWG_PREV_TMPDIR list=awg-proxy-env value="" +< add key=AWG_REMOTE list=awg-proxy-env value=109.107.177.79:55055 +< add key=AWG_S1 list=awg-proxy-env value=90 +< add key=AWG_S2 list=awg-proxy-env value=136 +< add key=AWG_S3 list=awg-proxy-env value=45 +< add key=AWG_S4 list=awg-proxy-env value=27 +< add key=AWG_SERVER_PUB list=awg-proxy-env value=\ +--- +> add key=AWG_I2 list=awg-MikroNuc-env value="" +> add key=AWG_I3 list=awg-MikroNuc-env value="" +> add key=AWG_I4 list=awg-MikroNuc-env value="" +> add key=AWG_I5 list=awg-MikroNuc-env value="" +> add key=AWG_JC list=awg-MikroNuc-env value=9 +> add key=AWG_JMAX list=awg-MikroNuc-env value=920 +> add key=AWG_JMIN list=awg-MikroNuc-env value=105 +> add key=AWG_LISTEN list=awg-MikroNuc-env value=:51820 +> add key=AWG_PREV_DNS list=awg-MikroNuc-env value=10.9.0.1 +> add key=AWG_PREV_TMPDIR list=awg-MikroNuc-env value="" +> add key=AWG_REMOTE list=awg-MikroNuc-env value=109.107.177.79:55055 +> add key=AWG_S1 list=awg-MikroNuc-env value=90 +> add key=AWG_S2 list=awg-MikroNuc-env value=136 +> add key=AWG_S3 list=awg-MikroNuc-env value=45 +> add key=AWG_S4 list=awg-MikroNuc-env value=27 +> add key=AWG_SERVER_PUB list=awg-MikroNuc-env value=\ +4776c4785,4789 +< add bridge=Bridge-LAN interface=ether2 +--- +> add bridge=Bridge-LAN interface=*C4 +> add bridge=Bridge-LAN interface=*C2 +> add bridge=Bridge-LAN interface=*C5 +> add bridge=Bridge-LAN interface=*C1 +> add bridge=Bridge-LAN interface=*C9 +4785c4798 +< add interface=*D9 list=Internet +--- +> add interface=wg-awg-MikroNuc list=Internet +4787,4789c4800 +< add interface=*3 list=LAN +< /interface wifi cap +< set discovery-interfaces=Bridge-LAN +--- +> add interface=Bridge-LAN list=LAN +4793,4795c4804,4807 +< add allowed-address=0.0.0.0/0 endpoint-address=172.18.9.106 endpoint-port=\ +< 51820 interface=wg-awg-proxy name=peer14 persistent-keepalive=10s \ +< public-key="o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +--- +> add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ +> 172.28.232.14 endpoint-port=51820 interface=wg-awg-MikroNuc name=docker \ +> persistent-keepalive=10s public-key=\ +> "o5IgCl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=" +4798,4799c4810,4813 +< add address=172.18.9.105/30 interface=veth-awg-proxy network=172.18.9.104 +< add address=10.9.0.3 interface=wg-awg-proxy network=10.9.0.3 +--- +> add address=172.28.232.13/30 interface=veth-awg-MikroNuc network=\ +> 172.28.232.12 +> add address=10.9.0.3/24 interface=wg-awg-MikroNuc network=10.9.0.0 +> add address=10.1.0.1/24 interface=*DA network=10.1.0.0 +4802,4803c4816,4818 +< dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ +< lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +--- +> 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 +4806,4808c4821,4822 +< doh-max-server-connections=20 servers=10.9.0.1 verify-doh-cert=yes +< /ip dns static +< add address=109.107.177.79 name=vds.vados.ru type=A +--- +> doh-max-server-connections=20 servers=\ +> 127.0.0.1,10.30.30.33,109.107.177.79 verify-doh-cert=yes +4810c4824,4825 +< add action=masquerade chain=srcnat src-address=172.18.9.104/30 +--- +> add action=masquerade chain=srcnat src-address=172.18.0.0/30 +> add action=masquerade chain=srcnat src-address=172.28.232.12/30 +4818c4833 +< add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ +--- +> add check-gateway=none disabled=no distance=2 dst-address=0.0.0.0/0 gateway=\ +4820,4821c4835,4838 +< add comment=awg-proxy-dns distance=1 dst-address=10.9.0.1/32 gateway=\ +< wg-awg-proxy +--- +> add comment=awg-MikroNuc-dns distance=1 dst-address=10.9.0.1/32 gateway=\ +> wg-awg-MikroNuc +> add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ +> wg-awg-MikroNuc routing-table=To-AWG scope=30 target-scope=10 +4839c4856 +< add interface=*3 type=external +--- +> add interface=Bridge-LAN type=external +4851,4852d4867 +< /system gps +< set set-system-time=yes -- 2.52.0