From fa5b2676a185916efd92092bccaa6645ade9c081 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Sun, 21 Jun 2026 21:00:04 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jun 21 21:00:04. --- .../20260620/20260620020003-Config.rsc | 3679 +++++++++++++ .../20260621/20260621030002-Config.rsc | 3680 +++++++++++++ .../20260621/20260621032002-Config.rsc | 3679 +++++++++++++ .../20260621/20260621080002-Config.rsc | 3679 +++++++++++++ .../20260621/20260621210001-Binary.backup | Bin 0 -> 363887 bytes MikroNUC/20260621/20260621210001-Config.rsc | 4880 +++++++++++++++++ .../20260621/20260621210001-Diff-Config.rsc | 8 + 7 files changed, 19605 insertions(+) create mode 100644 MikroMaster/20260620/20260620020003-Config.rsc create mode 100644 MikroMaster/20260621/20260621030002-Config.rsc create mode 100644 MikroMaster/20260621/20260621032002-Config.rsc create mode 100644 MikroMaster/20260621/20260621080002-Config.rsc create mode 100644 MikroNUC/20260621/20260621210001-Binary.backup create mode 100644 MikroNUC/20260621/20260621210001-Config.rsc create mode 100644 MikroNUC/history/20260621/20260621210001-Diff-Config.rsc diff --git a/MikroMaster/20260620/20260620020003-Config.rsc b/MikroMaster/20260620/20260620020003-Config.rsc new file mode 100644 index 0000000..63745e4 --- /dev/null +++ b/MikroMaster/20260620/20260620020003-Config.rsc @@ -0,0 +1,3679 @@ +# 2026-06-20 02:00:05 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2442/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5260/20-Ceee/ac/DP(18dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/694/a1b071a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:04 15.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set use-ip-firewall=yes +/ip firewall connection tracking +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set udp-timeout=10s +/ip neighbor discovery-settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Bridge-LAN list=LAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ + 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=1.0.0.1,8.8.4.4 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 match-subdomain=yes name=mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 name=MikroYellow type=A +add address=10.30.30.85 name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=wg.vados.ru list=VPN-Server +/ip firewall filter +add action=accept chain=input dst-port=18880 in-interface=Bridge-LAN \ + protocol=tcp src-address=10.30.30.0/24 +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 in-interface=Bridge-LAN protocol=tcp src-address=\ + 10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address=\ + 10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" dst-address-list=\ + VPN-Server out-interface="ether1 - Unet" protocol=udp src-port=13231 +add action=accept chain=output dst-address-list=VPN-Server dst-port=29185 \ + out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input protocol=icmp src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related in-interface-list=\ + *2000014 +add action=drop chain=input in-interface=!Bridge-LAN +/ip firewall mangle +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 \ + src-address-list=NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" dst-address=\ + !10.30.30.0/24 out-interface="ether1 - Unet" src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp src-address=\ + !10.30.30.0/24 to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing bfd configuration +add addresses=10.8.0.0/24 disabled=no forbid-bfd=yes interfaces=*E min-rx=\ + 200ms min-tx=200ms multiplier=5 vrf=main +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260621/20260621030002-Config.rsc b/MikroMaster/20260621/20260621030002-Config.rsc new file mode 100644 index 0000000..f704d6b --- /dev/null +++ b/MikroMaster/20260621/20260621030002-Config.rsc @@ -0,0 +1,3680 @@ +# 2026-06-21 03:00:05 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2442/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5260/20-Ceee/ac/DP(18dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/694/a1b071a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:04 15.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set use-ip-firewall=yes +/ip firewall connection tracking +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set udp-timeout=10s +/ip neighbor discovery-settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Bridge-LAN list=LAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ + 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=1.0.0.1,8.8.4.4 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 match-subdomain=yes name=mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 name=MikroYellow type=A +add address=10.30.30.85 name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=wg.vados.ru list=VPN-Server +/ip firewall filter +add action=accept chain=input dst-port=18880 in-interface=Bridge-LAN \ + protocol=tcp src-address=10.30.30.0/24 +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 in-interface=Bridge-LAN protocol=tcp src-address=\ + 10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address=\ + 10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" dst-address-list=\ + VPN-Server out-interface="ether1 - Unet" protocol=udp src-port=13231 +add action=accept chain=output dst-address-list=VPN-Server dst-port=29185 \ + out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input protocol=icmp src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related in-interface-list=\ + *2000014 +add action=drop chain=input in-interface=!Bridge-LAN +/ip firewall mangle +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 \ + src-address-list=NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" dst-address=\ + !10.30.30.0/24 out-interface="ether1 - Unet" src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp src-address=\ + !10.30.30.0/24 to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing bfd configuration +add addresses=10.8.0.0/24 disabled=no forbid-bfd=yes interfaces=*E min-rx=\ + 200ms min-tx=200ms multiplier=5 vrf=main +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl +#error exporting "/system/routerboard/settings" (timeout) +/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/20260621/20260621032002-Config.rsc b/MikroMaster/20260621/20260621032002-Config.rsc new file mode 100644 index 0000000..19060cc --- /dev/null +++ b/MikroMaster/20260621/20260621032002-Config.rsc @@ -0,0 +1,3679 @@ +# 2026-06-21 03:20:06 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2442/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5260/20-Ceee/ac/DP(18dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/694/a1b071a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:04 15.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set use-ip-firewall=yes +/ip firewall connection tracking +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set udp-timeout=10s +/ip neighbor discovery-settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Bridge-LAN list=LAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ + 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=1.0.0.1,8.8.4.4 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 match-subdomain=yes name=mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 name=MikroYellow type=A +add address=10.30.30.85 name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=wg.vados.ru list=VPN-Server +/ip firewall filter +add action=accept chain=input dst-port=18880 in-interface=Bridge-LAN \ + protocol=tcp src-address=10.30.30.0/24 +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 in-interface=Bridge-LAN protocol=tcp src-address=\ + 10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address=\ + 10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" dst-address-list=\ + VPN-Server out-interface="ether1 - Unet" protocol=udp src-port=13231 +add action=accept chain=output dst-address-list=VPN-Server dst-port=29185 \ + out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input protocol=icmp src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related in-interface-list=\ + *2000014 +add action=drop chain=input in-interface=!Bridge-LAN +/ip firewall mangle +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 \ + src-address-list=NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" dst-address=\ + !10.30.30.0/24 out-interface="ether1 - Unet" src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp src-address=\ + !10.30.30.0/24 to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing bfd configuration +add addresses=10.8.0.0/24 disabled=no forbid-bfd=yes interfaces=*E min-rx=\ + 200ms min-tx=200ms multiplier=5 vrf=main +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260621/20260621080002-Config.rsc b/MikroMaster/20260621/20260621080002-Config.rsc new file mode 100644 index 0000000..167cf5c --- /dev/null +++ b/MikroMaster/20260621/20260621080002-Config.rsc @@ -0,0 +1,3679 @@ +# 2026-06-21 08:00:05 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 2442/20-Ce/gn(18dBm), SSID: vados, local forwarding +set [ find default-name=wlan1 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados wireless-protocol=802.11 +# managed by CAPsMAN +# channel: 5260/20-Ceee/ac/DP(18dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add dns-servers=1.1.1.1,1.0.0.1 name=blsp.at verify-doh-cert=no +add comment=Kra dns-servers=1.0.0.1,1.1.1.1 name=slon2.to verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=no +add dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=no +add dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/694/a1b071a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:04 15.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set use-ip-firewall=yes +/ip firewall connection tracking +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set udp-timeout=10s +/ip neighbor discovery-settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +# ipv6 neighbor configuration has changed, please restart the device in order to apply the new settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=static internet-interface-list=Internet \ + lan-interface-list=LAN request-interval=1m wan-interface-list=WAN +/interface l2tp-server server +set authentication=mschap2 use-ipsec=yes +/interface list member +add interface=Bridge-LAN list=LAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:2 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.81 domain=WORKGROUP gateway=10.30.30.81 netmask=24 ntp-server=\ + 10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=1.0.0.1,8.8.4.4 verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 match-subdomain=yes name=mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 name=MikroYellow type=A +add address=10.30.30.85 name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=wg.vados.ru list=VPN-Server +/ip firewall filter +add action=accept chain=input dst-port=18880 in-interface=Bridge-LAN \ + protocol=tcp src-address=10.30.30.0/24 +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 in-interface=Bridge-LAN protocol=tcp src-address=\ + 10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address=\ + 10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" dst-address-list=\ + VPN-Server out-interface="ether1 - Unet" protocol=udp src-port=13231 +add action=accept chain=output dst-address-list=VPN-Server dst-port=29185 \ + out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input protocol=icmp src-address-list=VDSina +add action=accept chain=input connection-state=established +add action=accept chain=input connection-state=related in-interface-list=\ + *2000014 +add action=drop chain=input in-interface=!Bridge-LAN +/ip firewall mangle +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main passthrough=no src-address=10.30.30.0/24 \ + src-address-list=NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 src-address-list=!NoWG +add action=mark-connection chain=prerouting comment=\ + "Mark VPN connections to exclude from FastTrack" new-connection-mark=\ + VPN_No_FastTrack routing-mark=To-VPN +add action=change-mss chain=forward comment="MSS on WG" new-mss=1380 \ + out-interface=WG protocol=tcp routing-mark=To-VPN tcp-flags=syn tcp-mss=\ + 1381-65535 +add action=mark-routing chain=prerouting comment="VPN Network" dst-address=\ + 10.8.0.0/24 new-routing-mark=To-VPN src-address=10.30.30.0/24 +/ip firewall nat +add action=redirect chain=dstnat comment="udp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=udp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 to-addresses=\ + 10.30.30.81 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" dst-address=\ + !10.30.30.0/24 out-interface="ether1 - Unet" src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp src-address=\ + !10.30.30.0/24 to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 disabled=yes +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required \ + strong-crypto=yes +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing bfd configuration +add addresses=10.8.0.0/24 disabled=no forbid-bfd=yes interfaces=*E min-rx=\ + 200ms min-tx=200ms multiplier=5 vrf=main +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroNUC/20260621/20260621210001-Binary.backup b/MikroNUC/20260621/20260621210001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..111e0261c65e7a7a10d027af7e6e6efdd0f568d1 GIT binary patch literal 363887 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%0DL`(F?0F|uK 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%0Aeu0^fnD?0D%QIqbIowBvT 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%0Ah3MbR8jzhMO)G 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

E~+&E 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

C@FFUkYt=gfZZZ%CkO!4q*p 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

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