From 9e35dda23498ab933e82e9eec34ad0f4ed60fe47 Mon Sep 17 00:00:00 2001 From: vados-dev Date: Fri, 3 Jul 2026 00:40:03 +0300 Subject: [PATCH] Backup for MikroNUC created successfully at Jul 03 00:40:03. --- .../20260702/20260702210002-Config.rsc | 3695 ++++++++++++ .../20260702/20260702214001-Config.rsc | 3696 ++++++++++++ .../20260702/20260702220002-Config.rsc | 3695 ++++++++++++ .../20260702/20260702232002-Config.rsc | 3696 ++++++++++++ .../20260702/20260702234001-Config.rsc | 3697 ++++++++++++ .../20260703/20260703000002-Config.rsc | 3695 ++++++++++++ .../20260703/20260703004001-Binary.backup | Bin 0 -> 376391 bytes MikroNUC/20260703/20260703004001-Config.rsc | 5317 +++++++++++++++++ .../20260703/20260703004001-Diff-Config.rsc | 80 + 9 files changed, 27571 insertions(+) create mode 100644 MikroMaster/20260702/20260702210002-Config.rsc create mode 100644 MikroMaster/20260702/20260702214001-Config.rsc create mode 100644 MikroMaster/20260702/20260702220002-Config.rsc create mode 100644 MikroMaster/20260702/20260702232002-Config.rsc create mode 100644 MikroMaster/20260702/20260702234001-Config.rsc create mode 100644 MikroMaster/20260703/20260703000002-Config.rsc create mode 100644 MikroNUC/20260703/20260703004001-Binary.backup create mode 100644 MikroNUC/20260703/20260703004001-Config.rsc create mode 100644 MikroNUC/history/20260703/20260703004001-Diff-Config.rsc diff --git a/MikroMaster/20260702/20260702210002-Config.rsc b/MikroMaster/20260702/20260702210002-Config.rsc new file mode 100644 index 0000000..c36d263 --- /dev/null +++ b/MikroMaster/20260702/20260702210002-Config.rsc @@ -0,0 +1,3695 @@ +# 2026-07-02 21:00:07 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 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: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon2.to \ + verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/727/203f86a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:05 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=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=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:1 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,8.8.8.8 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN routing-mark=!main src-address=10.30.30.0/24 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 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260702/20260702214001-Config.rsc b/MikroMaster/20260702/20260702214001-Config.rsc new file mode 100644 index 0000000..5570a22 --- /dev/null +++ b/MikroMaster/20260702/20260702214001-Config.rsc @@ -0,0 +1,3696 @@ +# 2026-07-02 21:40:07 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# 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: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon2.to \ + verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/727/203f86a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:05 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=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=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:1 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,8.8.8.8 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN routing-mark=!main src-address=10.30.30.0/24 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 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +#error exporting "/ip/ipsec/key/psk" (timeout) +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260702/20260702220002-Config.rsc b/MikroMaster/20260702/20260702220002-Config.rsc new file mode 100644 index 0000000..0917415 --- /dev/null +++ b/MikroMaster/20260702/20260702220002-Config.rsc @@ -0,0 +1,3695 @@ +# 2026-07-02 22:00:07 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 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: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon2.to \ + verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/727/203f86a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:05 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=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=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:1 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,8.8.8.8 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN routing-mark=!main src-address=10.30.30.0/24 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 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260702/20260702232002-Config.rsc b/MikroMaster/20260702/20260702232002-Config.rsc new file mode 100644 index 0000000..3522163 --- /dev/null +++ b/MikroMaster/20260702/20260702232002-Config.rsc @@ -0,0 +1,3696 @@ +# 2026-07-02 23:20:16 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: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon2.to \ + verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/727/203f86a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:05 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=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=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:1 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,8.8.8.8 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN routing-mark=!main src-address=10.30.30.0/24 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 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +#error exporting "/ip/nat-pmp/interfaces" (timeout) +/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 +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260702/20260702234001-Config.rsc b/MikroMaster/20260702/20260702234001-Config.rsc new file mode 100644 index 0000000..f959f3e --- /dev/null +++ b/MikroMaster/20260702/20260702234001-Config.rsc @@ -0,0 +1,3697 @@ +# 2026-07-02 23:40: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: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon2.to \ + verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/727/203f86a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:05 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=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=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:1 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,8.8.8.8 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN routing-mark=!main src-address=10.30.30.0/24 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 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +#error exporting "/ip/ipsec/key/psk" (timeout) +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required +/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 +#error exporting "/mpls/mangle" (timeout) +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroMaster/20260703/20260703000002-Config.rsc b/MikroMaster/20260703/20260703000002-Config.rsc new file mode 100644 index 0000000..015c678 --- /dev/null +++ b/MikroMaster/20260703/20260703000002-Config.rsc @@ -0,0 +1,3695 @@ +# 2026-07-03 00:00:07 by RouterOS 7.23.1 +# software id = A8L5-07YF +# +# model = RB952Ui-5ac2nD +# serial number = D3D50CEDCD38 +/interface bridge +add admin-mac=48:8F:5A:8E:1A:BF auto-mac=no dhcpv6-agent-circuit-id=\ + "\$(INTERFACE):\$(VID)" dhcpv6-agent-remote-id="\$(BRIDGEMAC)" name=\ + Bridge-LAN port-cost-mode=short +/interface ethernet +set [ find default-name=ether1 ] comment="WAN - Unet" loop-protect=off name=\ + "ether1 - Unet" rx-flow-control=auto tx-flow-control=auto +set [ find default-name=ether2 ] comment=LAN-2 +set [ find default-name=ether3 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-3 +set [ find default-name=ether4 ] comment=LAN-4 +set [ find default-name=ether5 ] advertise="10M-baseT-half,10M-baseT-full,100M\ + -baseT-half,100M-baseT-full,1G-baseT-full,1G-baseX" comment=LAN-5 \ + loop-protect=on poe-out=off rx-flow-control=auto tx-flow-control=auto +/interface wireless +# managed by CAPsMAN +# channel: 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: 5200/20-eCee/ac/P(21dBm), SSID: vados5g, local forwarding +set [ find default-name=wlan2 ] country=belarus disabled=no mode=ap-bridge \ + ssid=vados5g wireless-protocol=802.11 +/interface wireguard +add comment=WG listen-port=13231 mtu=1320 name=WG +/interface list +add include=static name=LAN +add name=Internet +add include=Internet name=WAN +/interface wireless security-profiles +set [ find default=yes ] authentication-types=wpa-psk,wpa2-psk mode=\ + dynamic-keys supplicant-identity=MikroTik +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=HP1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +add name=VADOS-LEN options=HostName +add name=VADOS-NUC options=HostName +add name=MikroNUC options=HostName +add name=master options=HostName +add name=MikroYellow options=HostName +add name="=\"Client ID\"" options=HostName +add name=iVados options=HostName +/ip dns forwarders +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=blsp.at verify-doh-cert=no +add comment=Kra disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon2.to \ + verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=slon3.at verify-doh-cert=no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=slon3.cc verify-doh-cert=no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2web.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.1.1.1,1.0.0.1 name=bs2best.at verify-doh-cert=\ + no +add disabled=yes dns-servers=1.0.0.1,1.1.1.1 name=psrms.is verify-doh-cert=no +/ip ipsec proposal +set [ find default=yes ] disabled=yes +/ip pool +add name=dhcp ranges=10.30.30.83-10.30.30.254 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set interface=Bridge-LAN lease-time=10m \ + name=masterDHCP server-address=10.30.30.81 +/ip smb users +set [ find default=yes ] disabled=yes +add disabled=yes name=vados +/ipv6 dhcp-client option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=DUID value=0x00030001488f5a8e1abc +add code=61 name=clientid +/ipv6 dhcp-server option +add code=119 name=domain-search-list value="0x03'WORKGROUP'0x00" +add code=12 name=HostName value="\$(HOSTNAME)" +add code=61 name=CLIENT_DUID +add code=61 name=CLIENT_MAC +/ppp profile +set *FFFFFFFE use-upnp=no +/routing bgp template +set default disabled=no output.network=bgp-networks +/routing table +add disabled=no fib name=To-VPN +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=fetchCustom owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.86.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.86.pub\";\ + \n\r\ + \n#:local Script AM-setup\ + \n;\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n\ + \n\r\ + \n#\ + \n\r\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n\r\ + \n#\r\ + \n\ + \n#root.mnuc.backuper\ + \n\ + \n\r\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@10\ + .30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\r\ + \n\ + \n#:local Script AM-GlobalConfig;\ + \n\r\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\r\ + \n\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n\r\ + \n\r\ + \n#\ + \n:global MkDir;\ + \n\r\ + \n\ + \n#:local FetchFile Binary.backup\ + \n:local localDir \"\";\ + \n\ + \n\r\ + \n#\$MkDir \$localDir;\r\ + \n\ + \n#\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/727/203f86a6\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 13:30:05 29.06.2026.\";\r\ + \n\r\ + \n# The variables are received right here.\r\ + \n:global GlobalConfVersion 140;\r\ + \n\r\ + \n:global SumsDirAndFileName \"tmp/checksums.json\";\r\ + \n\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global IdentityShort [:pick \$Identity 0 18];\r\ + \n\r\ + \n:global devBoardName [/system/resource/get board-name];\r\ + \n:global devMode [/system/device-mode/get mode];\r\ + \n:global devCurArch [/system/resource/get architecture-name];\r\ + \n:global devCurVersion [/system/resource/get version];\r\ + \n\r\ + \n# Variables defined in AM-GlobalConfig.rsc\r\ + \n:global TerminalColorOutput;\r\ + \n:global CommentsInScripts;\r\ + \n:global DetectIpAddrDefault;\r\ + \n:global DetectIpAddrFallback;\r\ + \n:global updateChannel;\r\ + \n:global Domain;\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n\r\ + \n# Single functions, without nested functions.\r\ + \n:global MiniDateTimeStamp;\r\ + \n:global MicroDateTimeStamp;\r\ + \n\r\ + \n# Include globals \$PrintDebug and \$PrintDebugOverride\r\ + \n:global LogPrint;\r\ + \n\r\ + \n# DeviceInfo and Updates\r\ + \n:global IsCheckUpdates;\r\ + \n:global WaitCheckUpdates;\r\ + \n:global GetPubIp;\r\ + \n\r\ + \n:global GetArrDeviceInfo;\r\ + \n:global WaitArrDeviceInfo;\r\ + \n\r\ + \n:global devModel;\r\ + \n:global devRunningMode;\r\ + \n\r\ + \n:global devRbRevision;\r\ + \n:global devRbSerialNumber;\r\ + \n:global devRbVerAvail;\r\ + \n\r\ + \n:global runningChannelVersion;\r\ + \n:global runningChannel;\r\ + \n:global runningVersion;\r\ + \n\r\ + \n:global devBuildTime;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n:global currentUpdateChannel;\r\ + \n\r\ + \n:global ScriptInstallUpdate;\r\ + \n\r\ + \n:global DeviceInfo;\r\ + \n\r\ + \n:global OldMode;\r\ + \n\r\ + \n:global ParseKeyValueStore;\r\ + \n:global RandomDelay;\r\ + \n:global GetRandomNumber;\r\ + \n:global EscapeForRegEx;\r\ + \n:global CharacterMultiply;\r\ + \n:global CharacterReplace;\r\ + \n:global CleanFilePath;\r\ + \n:global CleanName;\r\ + \n\r\ + \n:global CommitBrief;\r\ + \n\r\ + \n:global EitherOr;\r\ + \n:global FormatLine;\r\ + \n:global GetRandom20CharAlNum;\r\ + \n:global GetRandom20CharHex;\r\ + \n:global GetRandomNumber;\r\ + \n:global Grep;\r\ + \n:global IfThenElse;\r\ + \n:global MAX;\r\ + \n:global MIN;\r\ + \n:global HumanReadableNum;\r\ + \n\r\ + \n:global IsFullyConnected;\r\ + \n:global IsDNSResolving;\r\ + \n:global IsTimeSync;\r\ + \n:global IsTimeSyncCached;\r\ + \n:global IsTimeSyncResetNtp;\r\ + \n\r\ + \n:global MkDir;\r\ + \n:global RmDir;\r\ + \n:global RmFile;\r\ + \n:global FileGet;\r\ + \n:global Unix2Dos;\r\ + \n:global UrlEncode;\r\ + \n:global WaitForFile;\r\ + \n:global ScriptLock;\r\ + \n:global SendTelegram;\r\ + \n:global NotificationFunctions;\r\ + \n:global SendNotification;\r\ + \n:global SendNotification2;\r\ + \n:global SymbolForNotification;\r\ + \n:global SymbolByUnicodeName;\r\ + \n:global ValidateSyntax;\r\ + \n:global CheckSumsVerify;\r\ + \n:global GlobalConfChanges;\r\ + \n:global GlobalConfMigration;\r\ + \n:global VersionToNum;\r\ + \n:global RequiredRouterOS;\r\ + \n:global ExitError;\r\ + \n:global ExitOnError;\r\ + \n\r\ + \n:global FetchHugeFiles;\r\ + \n:global FetchUserAgentStr;\r\ + \n:global FileExists;\r\ + \n:global CertificateAvailable;\r\ + \n:global CertificateDownload;\r\ + \n:global CertificateNameByCN;\r\ + \n:global CharacterMultiply;\r\ + \n\r\ + \n:global InspectVar;\r\ + \n:global InspectVarRet;\r\ + \n:global ParseCustom;\r\ + \n\r\ + \n:set IsCheckUpdates do={\r\ + \n :local isInfo [:tobool \$1];\r\ + \n :global updateChannel;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :log info (\$0 . \" \\\$isInfo = \" . \$isInfo); \r\ + \n :local CurrentChannel [ /system/package/update/get channel ];\r\ + \n :if ([ :len \$updateChannel ] > 0 && \$CurrentChannel != \$updateChann\ + el) do={\r\ + \n :retry command={/system/package/update set channel=\$updateChannel} \ + \_delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" Set updates channel to: \$updateChannel\");\r\ + \n }\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local Channel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set currentUpdateChannel \$Channel;\r\ + \n :log info (\$0 . \" Update Channel: \" . \$Channel . \".\");\r\ + \n :local StatusPkg \"undefined\";\r\ + \n :log info (\$0 . \" Checking for updates...\");\r\ + \n :retry command={:set \$StatusPkg ([/system/package/update/check-for-up\ + dates without-paging as-value]->\"status\")} \\\r\ + \n delay=300ms max=5 on-error={};\r\ + \n :log info (\$0 . \" \\\$StatusPkg = \" . \$StatusPkg);\r\ + \n :if (\$StatusPkg = \"New version is available\") do={ :set PkgUpdStatu\ + s true; } else={ :set PkgUpdStatus false; }\r\ + \n :set ROsVerAvail [ :tostr [ /system/package/update/get latest-version \ + ] ];\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if (\$isInfo != false || \$PkgUpdStatus = true) do={\r\ + \n :log info (\$0 . \" \\\$PkgUpdStatus = \" . \$PkgUpdStatus);\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :retry command={:set (\$arrDeviceInfo->\"devUpdates\") [ /system/pac\ + kage/update/get ]} delay=500ms max=5 on-error={:log error (\"Error\")};\r\ + \n :return true;\r\ + \n } else={\r\ + \n :return \$PkgUpdStatus;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set WaitCheckUpdates do={\r\ + \n :global IsCheckUpdates;\r\ + \n :global PkgUpdStatus;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n :global arrDeviceInfo;\r\ + \n :local Status \"\";\r\ + \n :local Update;\r\ + \n :local WaitCount 0;\r\ + \n :if ([ \$IsCheckUpdates true ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n :while ((\$Status = \"\" || \$Status = \"finding out latest version...\ + \") && \$WaitCount < 60) do={\r\ + \n :delay 1s;\r\ + \n :set WaitCount (\$WaitCount + 1);\r\ + \n :set Status [ :tostr [ /system/package/update/get status ] ];\r\ + \n }\r\ + \n :set Update [ /system/package/update/get ];\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n :if ((\$Update->\"status\") = \"New version is available\") do={\r\ + \n :set PkgUpdStatus true;\r\ + \n } else={\r\ + \n :set PkgUpdStatus false;\r\ + \n }\r\ + \n :if ([ :typeof \$arrDeviceInfo ] != \"array\") do={\r\ + \n :set arrDeviceInfo [:toarray \$arrDeviceInfo];\r\ + \n }\r\ + \n :set (\$arrDeviceInfo->\"devUpdates\") \$Update;\r\ + \n :if (\$Status = \"\" || \$Status = \"finding out latest version...\") \ + do={\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set WaitArrDeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :while ([ \$GetArrDeviceInfo ] = false) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set GetArrDeviceInfo do={\r\ + \n :global LogPrint;\r\ + \n :global arrDeviceInfo;\r\ + \n :global devBoardName;\r\ + \n :global devModel;\r\ + \n :global devRunning;\r\ + \n :global devCurArch;\r\ + \n :global devRbRevision;\r\ + \n :global devRbSerialNumber;\r\ + \n :global devBuildTime;\r\ + \n :global runningChannelVersion;\r\ + \n :global runningChannel;\r\ + \n :global runningVersion;\r\ + \n :global currentUpdateChannel;\r\ + \n :global ROsVerAvail;\r\ + \n\r\ + \n :local DeviceInfo;\r\ + \n :local Resource [/system/resource/get];\r\ + \n :local Update [ /system/package/update/get ];\r\ + \n :local VersionString [ :tostr (\$Resource->\"version\") ];\r\ + \n :local ChannelStart [ :find \$VersionString \" (\" ];\r\ + \n :local RouterBoard;\r\ + \n :local IsRouterBoard false;\r\ + \n :local RunningMode \"\";\r\ + \n\r\ + \n :set devCurArch [ :tostr (\$Resource->\"architecture-name\") ];\r\ + \n :set devBuildTime [ :tostr (\$Resource->\"build-time\") ];\r\ + \n :set runningChannelVersion \$VersionString;\r\ + \n :set runningVersion \$VersionString;\r\ + \n :set runningChannel \"\";\r\ + \n\r\ + \n :if ([ :typeof \$ChannelStart ] = \"num\" && \$ChannelStart >= 0) do=\ + {\r\ + \n :set runningVersion [ :pick \$VersionString 0 \$ChannelStart ];\r\ + \n :local ChannelRest [ :pick \$VersionString (\$ChannelStart + 2) [ :\ + len \$VersionString ] ];\r\ + \n :local ChannelEnd [ :find \$ChannelRest \")\" ];\r\ + \n :if ([ :typeof \$ChannelEnd ] = \"num\" && \$ChannelEnd >= 0) do={\ + \r\ + \n :set runningChannel [ :pick \$ChannelRest 0 \$ChannelEnd ];\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([:pick \$devBoardName 0 3] != \"CHR\" or [:pick \$devBoardName 0\ + \_3] != \"x86\") do={\r\ + \n :do {\r\ + \n :set RouterBoard [[ :parse \"/system/routerboard/get\" ]];\r\ + \n# :set RouterBoard [ /system/routerboard/get ];\r\ + \n :if ((\$RouterBoard->\"routerboard\") = true) do={\r\ + \n :set IsRouterBoard true;\r\ + \n }\r\ + \n } on-error={}\r\ + \n }\r\ + \n\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get mode ];\r\ + \n } on-error={\r\ + \n :do {\r\ + \n :set RunningMode [ /system/device-mode/get running ];\r\ + \n } on-error={\r\ + \n :set RunningMode \"\";\r\ + \n }\r\ + \n }\r\ + \n :set devRunning \$RunningMode;\r\ + \n\r\ + \n :if (\$IsRouterBoard = true) do={\r\ + \n :set devModel [ :tostr (\$RouterBoard->\"model\") ];\r\ + \n :set devRbRevision [ :tostr (\$RouterBoard->\"revision\") ];\r\ + \n :set devRbSerialNumber [ :tostr (\$RouterBoard->\"serial-number\") \ + ];\r\ + \n } else={\r\ + \n :set devModel \$devBoardName;\r\ + \n :set devRbRevision \"\";\r\ + \n :set devRbSerialNumber \"\";\r\ + \n }\r\ + \n\r\ + \n :set currentUpdateChannel [ :tostr (\$Update->\"channel\") ];\r\ + \n :set ROsVerAvail [ :tostr (\$Update->\"latest-version\") ];\r\ + \n\r\ + \n :set (\$DeviceInfo->\"board-name\") \$devBoardName;\r\ + \n :set (\$DeviceInfo->\"architecture-name\") \$devCurArch;\r\ + \n :set (\$DeviceInfo->\"model\") \$devModel;\r\ + \n :set (\$DeviceInfo->\"revision\") \$devRbRevision;\r\ + \n :set (\$DeviceInfo->\"serial-number\") \$devRbSerialNumber;\r\ + \n :set (\$DeviceInfo->\"running-mode\") \$devRunning;\r\ + \n :set (\$DeviceInfo->\"build-time\") \$devBuildTime;\r\ + \n :set (\$DeviceInfo->\"running-channel-version\") \$runningChannelVers\ + ion;\r\ + \n :set (\$DeviceInfo->\"running-version\") \$runningVersion;\r\ + \n :set (\$DeviceInfo->\"running-channel\") \$runningChannel;\r\ + \n :set (\$DeviceInfo->\"routerboard\") \$IsRouterBoard;\r\ + \n :set (\$DeviceInfo->\"current-firmware\") [ :tostr (\$RouterBoard->\"\ + current-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"upgrade-firmware\") [ :tostr (\$RouterBoard->\"\ + upgrade-firmware\") ];\r\ + \n :set (\$DeviceInfo->\"package-channel\") \$currentUpdateChannel;\r\ + \n :set (\$DeviceInfo->\"installed-version\") [ :tostr (\$Update->\"inst\ + alled-version\") ];\r\ + \n :set (\$DeviceInfo->\"latest-version\") \$ROsVerAvail;\r\ + \n :set (\$DeviceInfo->\"update-status\") [ :tostr (\$Update->\"status\"\ + ) ];\r\ + \n\r\ + \n :set arrDeviceInfo \$DeviceInfo;\r\ + \n :return \$DeviceInfo;\r\ + \n}\r\ + \n\r\ + \n:set DeviceInfo do={\r\ + \n :global GetArrDeviceInfo;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global IfThenElse;\r\ + \n :global FormatLine;\r\ + \n :global CommitBrief;\r\ + \n\r\ + \n :local License [/system/license/get];\r\ + \n :local Snmp [ /snmp/get ];\r\ + \n :local Info [ \$GetArrDeviceInfo ];\r\ + \n\r\ + \n :if ([ :typeof \$Info ] != \"array\") do={\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :return ( \\\r\ + \n [ \$FormatLine \"Hostname\" \$Identity ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"location\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Location\" (\$Snmp->\"location\") ] . \"\\n\") ]\ + \_. \\\r\ + \n [ \$IfThenElse ([ :len (\$Snmp->\"contact\") ] > 0) \\\r\ + \n ([ \$FormatLine \"Contact\" (\$Snmp->\"contact\") ] . \"\\n\") ] .\ + \_\\\r\ + \n \"Hardware:\\n\" . \\\r\ + \n [ \$FormatLine \" Board\" (\$Info->\"board-name\") ] . \"\\n\" . \ + \\\r\ + \n [ \$FormatLine \" Arch\" (\$Info->\"architecture-name\") ] . \"\\\ + n\" . \\\r\ + \n [ \$FormatLine \" Model\" (\$Info->\"model\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"revision\") ] > 0) \\\r\ + \n (\" \" . (\$Info->\"revision\")) ] . \"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"serial-number\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Serial\" (\$Info->\"serial-number\") ] . \"\ + \\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"nlevel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"nlevel\ + \")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS:\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$License->\"level\") ] > 0) \\\r\ + \n ([ \$FormatLine \" License\" (\"level \" . (\$License->\"level\ + \")) ] . \"\\n\") ] . \\\r\ + \n [ \$FormatLine \" Channel\" (\$Info->\"package-channel\") ] . \"\ + \\n\" . \\\r\ + \n [ \$FormatLine \" Installed\" (\$Info->\"installed-version\") ] .\ + \_\"\\n\" . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"running-channel\") ] > 0) \\\r\ + \n ([ \$FormatLine \" Running\" ((\$Info->\"running-version\") . \ + \" (\" . (\$Info->\"running-channel\") . \")\") ] . \"\\n\") \\\r\ + \n ([ \$FormatLine \" Running\" (\$Info->\"running-version\") ] . \ + \"\\n\") ] . \\\r\ + \n [ \$IfThenElse ([ :len (\$Info->\"latest-version\") ] > 0 && \\\r\ + \n (\$Info->\"installed-version\") != (\$Info->\"latest-version\"))\ + \_\\\r\ + \n ([ \$FormatLine \" Available\" (\$Info->\"latest-version\") ] .\ + \_\"\\n\") ] . \\\r\ + \n [ \$IfThenElse ((\$Info->\"routerboard\") = true && \\\r\ + \n (\$Info->\"current-firmware\") != (\$Info->\"upgrade-firmware\")\ + ) \\\r\ + \n ([ \$FormatLine \" Firmware\" ((\$Info->\"current-firmware\") .\ + \_\" -> \" . (\$Info->\"upgrade-firmware\")) ] . \"\\n\") ] . \\\r\ + \n \"RouterOS-Scripts:\\n\" . \\\r\ + \n [ \$FormatLine \" Commit\" [ \$CommitBrief ] ] . \"\\n\" . \\\r\ + \n [ \$FormatLine \" Version\" \$GlobalConfVersion ]);\r\ + \n}\r\ + \n\r\ + \n:if ([ :typeof \$NotificationFunctions ] != \"array\") do={:set Notifica\ + tionFunctions ({})}\r\ + \n\r\ + \n:set GetRandom20CharAlNum do={\r\ + \n :global EitherOr;\r\ + \n :return [ :rndstr length=[ \$EitherOr [ :tonum \$1 ] 20 ] from=\"ABCDE\ + FGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789\" ];\r\ + \n}\r\ + \n\r\ + \n:set GetRandom20CharHex do={:global EitherOr; :return [ :rndstr length=[\ + \_\$EitherOr [ :tonum \$1 ] 20 ] from=\"0123456789abcdef\" ]; }\r\ + \n:set GetRandomNumber do={:global EitherOr; :return [ :rndnum from=0 to=[\ + \_\$EitherOr [ :tonum \$1 ] 4294967295 ] ]; }\r\ + \n\r\ + \n:global checkRunJob;\r\ + \n:set checkRunJob do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLock; \r\ + \n :if ([ \$ScriptLock \$Script ] = false) do={:return false}\r\ + \n :foreach Job in=[ /system/script/job/find where script=\$Script ] do={\ + :set Job [ /system/script/job/get \$Job ];\r\ + \n :while ([ :typeof (\$Job->\"parent\") ] = \"id\") do={:set Job [ /sy\ + stem/script/job/get [ find where .id=(\$Job->\"parent\") ] ]}\r\ + \n :if ((\$Job->\"type\") = \"login\") do={:return true};}; :return fa\ + lse; \r\ + \n}\r\ + \n\r\ + \n:set InspectVar do={\r\ + \n :global InspectVarRet;\r\ + \n :put [ :tocrlf [ \$InspectVarRet \$1 ] ];\r\ + \n}\r\ + \n\r\ + \n:set CommitBrief do={\r\ + \n :global CommitId;\r\ + \n :global CommitInfo;\r\ + \n :if (\$CommitId = \"unknown\") do={\r\ + \n :return \"unknown\";\r\ + \n }\r\ + \n :return (\$CommitInfo . \"/\" . [ :pick \$CommitId 0 8 ]);\r\ + \n}\r\ + \n\r\ + \n:set InspectVarRet do={\r\ + \n :local Input \$1;\r\ + \n :local Level (0 + [ :tonum \$2 ]);\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :global InspectVarRet;\r\ + \n :local IndentReturn do={\r\ + \n :local Prefix [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Level [ :tonum \$3 ];\r\ + \n :global CharacterMultiply;\r\ + \n :return ([ \$CharacterMultiply \" \" (\$Level * 2) ] . \"-\" . \$Pre\ + fix . \"-> \" . \$Value);\r\ + \n }\r\ + \n :local TypeOf [ :typeof \$Input ];\r\ + \n :local Len [ :len \$Input ];\r\ + \n :local Return [ \$IndentReturn \"type\" \$TypeOf \$Level ];\r\ + \n :if (\$TypeOf = \"array\") do={\r\ + \n :foreach Key,Value in=\$Input do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"key\" \$Key (\$Level + 1) ] . \"\\n\" . \\\r\ + \n [ \$InspectVarRet \$Value (\$Level + 2) ]);\r\ + \n }\r\ + \n } else={\r\ + \n :if (\$TypeOf = \"str\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"len\" \$Len \$Level ]);\r\ + \n :if ([ :typeof [ :find \$Input (\"\\r\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\r\") \"\" ];\r\ + \n }\r\ + \n :if ([ :typeof [ :find \$Input (\"\\n\") ] ] = \"num\") do={\r\ + \n :set Input [ \$CharacterReplace \$Input (\"\\n\") \" \" ];\r\ + \n }\r\ + \n }\r\ + \n :if (\$TypeOf != \"nothing\") do={\r\ + \n :set \$Return (\$Return . \"\\n\" . \\\r\ + \n [ \$IndentReturn \"value\" [ \$IfThenElse ([ :len \$Input ] > 80)\ + \_\\\r\ + \n ([ :pick \$Input 0 77 ] . \"...\") \$Input ] \$Level ]);\r\ + \n }\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set GetPubIp do={\r\ + \n :global DetectIpAddrDefault;\r\ + \n :global DetectIpAddrFallback;\r\ + \n\r\ + \n :local PubIpAddr false;\r\ + \n :local Url \$DetectIpAddrDefault;\r\ + \n :local JobName [ :jobname ];\r\ + \n\r\ + \n :do {\r\ + \n :log debug (\$0 . \" Trying to detect public IP using default serv\ + ice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=50ms max=5 on-error={:log error (\$JobName . \" Failed to \ + detect public IP using default service: \" . \$Url . \".\")}\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed to detect public IP using default serv\ + ice: \" . \$Url . \".\");\r\ + \n :set Url \$DetectIpAddrFallBack;\r\ + \n :log debug (\$0 . \" Trying to detect public IP using fallback ser\ + vice: \" . \$Url . \"...\");\r\ + \n :retry command={:set PubIpAddr ([ /tool/fetch http-method=\"get\"\ + \_check-certificate=yes-without-crl \$Url output=user as-value ]->\"data\"\ + )} \\\r\ + \n delay=200ms max=5 on-error={:log error (\$JobName . \" Failed to\ + \_detect public IP using fallback service: \" . \$Url . \".\")}\r\ + \n };\r\ + \n :local IpLen ([ :len \$PubIpAddr ] -1);\r\ + \n :if (\$PubIpAddr = false) do={:set PubIpAddr \"not-detected\"} else=\ + {:set PubIpAddr [:pick \$PubIpAddr 0 \$IpLen]}\r\ + \n :return \$PubIpAddr;\r\ + \n}\r\ + \n\r\ + \n:set FormatLine do={\r\ + \n :local Key [ :tostr \$1 ];\r\ + \n :local Value [ :tostr \$2 ];\r\ + \n :local Indent [ :tonum \$3 ];\r\ + \n :local Spaces;\r\ + \n :local Return \"\";\r\ + \n :global CharacterMultiply;\r\ + \n :global EitherOr;\r\ + \n :set Indent [ \$EitherOr \$Indent 16 ];\r\ + \n :local Spaces [ \$CharacterMultiply \" \" \$Indent ];\r\ + \n :if ([ :len \$Key ] > 0) do={ :set Return (\$Key . \":\")}\r\ + \n :if ([ :len \$Key ] > (\$Indent - 2)) do={:set Return (\$Return . \"\\\ + n\" . [ :pick \$Spaces 0 \$Indent ] . \$Value);\r\ + \n } else={:set Return (\$Return . [ :pick \$Spaces 0 (\$Indent - [ :len \ + \$Return ]) ] . \$Value)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set LogPrint do={\r\ + \n :local Severity [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Message [ :tostr \$3 ];\r\ + \n :global PrintDebug;\r\ + \n :global PrintDebugOverride;\r\ + \n :global EitherOr;\r\ + \n :local Debug [ \$EitherOr (\$PrintDebugOverride->\$Name) \$PrintDebug \ + ];\r\ + \n :local PrintSeverity do={\r\ + \n :global TerminalColorOutput;\r\ + \n :if (\$TerminalColorOutput != true) do={:return \$1}\r\ + \n :local Color { debug=96; info=97; warning=93; error=91 };\r\ + \n :return (\"\\1B[\" . \$Color->\$1 . \"m\" . \$1 . \"\\1B[0m\");\r\ + \n}\r\ + \n :local Log ([ \$EitherOr \$Name \"\" ] . \": \" . \$Message);\ + \r\ + \n :if (\$Severity ~ (\"^(custom|debug|error|info)\\\$\")) do={\r\ + \n :if (\$Severity = \"debug\") do={:log debug \$Log}\r\ + \n :if (\$Severity = \"error\") do={:log error \$Log}\r\ + \n :if (\$Severity = \"info\" ) do={:log info \$Log}\r\ + \n } else={:log warning \$Log; :set Severity \"warning\"}\r\ + \n :if (\$Severity != \"debug\" || \$Debug = true) do={:put ([ \$PrintSev\ + erity \$Severity ] . \": \" . \$Message)}\r\ + \n}\r\ + \n\r\ + \n:set CertificateAvailable do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :local UseFor [ :tostr \$2 ];\r\ + \n\r\ + \n :global CertificateDownload;\r\ + \n :global ParseKeyValueStore;\r\ + \n\r\ + \n :if ([ :len \$UseFor ] = 0) do={\r\ + \n :log warning (\$0 . \"The intended use is undefined!\");\r\ + \n :set UseFor \"undefined\";\r\ + \n }\r\ + \n :if ([ /system/resource/get free-hdd-space ] < 8388608 && \\\r\ + \n [ /certificate/settings/get crl-download ] = true && \\\r\ + \n [ /certificate/settings/get crl-store ] = \"system\") do={\r\ + \n :log warning (\$0 . \"This system has low free flash space but \" . \ + \\\r\ + \n \"is configured to download certificate CRLs to system!\");\r\ + \n }\r\ + \n :if ([ :len \$CommonName ] = 0) do={\r\ + \n :log warning (\$0 . \"No CommonName given!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if (((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" || \\\ + \r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \$UseFor || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CommonName\ + \_] ] > 0) do={\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n :log info (\$0 . \"Certificate with CommonName '\" . \$CommonName . \ + \"' not available.\");\r\ + \n :if ([ \$CertificateDownload \$CommonName ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] > 1\ + ) do={\r\ + \n :log info (\$0 . \"There are \" . \$CertCount . \" Certificates with\ + \_CommonName '\" . \$CommonName . \"'. Should be ok.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :local CertVal [ /certificate/get [ find where common-name=\$CommonNam\ + e ] ];\r\ + \n :while ((\$CertVal->\"akid\") != \"\" && (\$CertVal->\"akid\") != (\$C\ + ertVal->\"skid\")) do={\r\ + \n :if ([ :len [ /certificate/find where skid=(\$CertVal->\"akid\") ] ]\ + \_= 0) do={\r\ + \n :local IssuerCN ([ \$ParseKeyValueStore (\$CertVal->\"issuer\") ]-\ + >\"CN\");\r\ + \n :log info (\$0 . \"Certificate chain for '\" . \$CommonName . \"' \ + is incomplete, missing '\" . \$IssuerCN . \"'.\");\r\ + \n :if ([ \$CertificateDownload \$IssuerCN ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :set CertVal [ /certificate/get [ find where skid=(\$CertVal->\"akid\ + \") ] ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateDownload do={\r\ + \n :local CommonName [ :tostr \$1 ];\r\ + \n :global ScriptUpdatesBaseUrl;\r\ + \n :global ScriptUpdatesUrlSuffix;\r\ + \n :global CertificateNameByCN;\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :log info (\$0 . \"Downloading and importing certificate with \" . \\\ + \r\ + \n \"CommonName '\" . \$CommonName . \"'.\");\r\ + \n :local FileName ([ \$CleanName \$CommonName ] . \".pem\");\r\ + \n :do {\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-header\ + -field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n (\$ScriptUpdatesBaseUrl . \"certs/\" . \$FileName . \$ScriptUpdate\ + sUrlSuffix) \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n } on-error={\r\ + \n :log warning (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \\\r\ + \n \"' from repository! Trying fallback to mkcert.org...\");\r\ + \n :do {\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where common-name=\"ISRG Root X1\"\ + \_] ] = 0 && \\\r\ + \n !(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" \ + || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \ + \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\ + \r\ + \n [ :len [ /certificate/builtin/find where common-name=\"ISRG\ + \_Root X1\" ] ] > 0)) do={\r\ + \n :log error (\$0 . \" Required certificate is not available.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n /tool/fetch check-certificate=yes-without-crl mode=https http-head\ + er-field=({ [ \$FetchUserAgentStr \$0 ] }) \\\r\ + \n url=\"https://mkcert.org/generate/\" http-data=[ :serialize to=jso\ + n ({ \$CommonName }) ] \\\r\ + \n dst-path=\$FileName as-value;\r\ + \n \$WaitForFile \$FileName;\r\ + \n :if ([ /file/get \$FileName size ] = 0) do={\r\ + \n \$RmFile \$FileName;\r\ + \n :error false;\r\ + \n }\r\ + \n } on-error={\r\ + \n :log error (\$0 . \" Failed downloading certificate with CommonNam\ + e '\" . \$CommonName . \"'!\");\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n /certificate/import file-name=\$FileName passphrase=\"\" as-value;\r\ + \n :delay 1s;\r\ + \n \$RmFile \$FileName;\r\ + \n :if ([ :len [ /certificate/find where common-name=\$CommonName ] ] = 0\ + ) do={\r\ + \n /certificate/remove [ find where name~(\"^\" . \$FileName . \"_[0-9]\ + +\\\$\") ];\r\ + \n :log warning (\$0 . \" Certificate with CommonName '\" . \$CommonNam\ + e . \"' still unavailable!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :foreach Cert in=[ /certificate/find where name~(\"^\" . \$FileName . \ + \"_[0-9]+\\\$\") ] do={\r\ + \n \$CertificateNameByCN [ /certificate/get \$Cert common-name ];\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CertificateNameByCN do={\r\ + \n :local Match [ :tostr \$1 ];\r\ + \n :global CleanName;\r\ + \n :local Cert ([ /certificate/find where (common-name=\$Match or fingerp\ + rint=\$Match or name=\$Match) ]->0);\r\ + \n :if ([ :len \$Cert ] = 0) do={\r\ + \n :log warning (\$0 .\" No matching certificate found.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local CommonName [ /certificate/get \$Cert common-name ];\r\ + \n /certificate/set \$Cert name=[ \$CleanName \$CommonName ];\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FileExists do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local Type [ :tostr \$2 ];\r\ + \n :global FileGet;\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={ :return false; }\r\ + \n :if ([ :len (\$FileVal->\"size\") ] = 0) do={ :return false; }\r\ + \n :if ([ :len \$Type ] = 0 || \$FileVal->\"type\" = \$Type) do={ :return\ + \_true; }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set Grep do={\r\ + \n :local Input ([ :tostr \$1 ] . \"\\n\");\r\ + \n :local Pattern [ :tostr \$2 ];\r\ + \n :if ([ :typeof [ :find \$Input \$Pattern ] ] = \"nil\") do={:return []\ + }\r\ + \n :do {\r\ + \n :local Line [ :pick \$Input 0 [ :find \$Input \"\\n\" ] ];\r\ + \n :if ([ :typeof [ :find \$Line \$Pattern ] ] = \"num\") do={:return \ + \$Line}\r\ + \n :set Input [ :pick \$Input ([ :find \$Input \"\\n\" ] + 1) [ :len \$\ + Input ] ];\r\ + \n } while=([ :len \$Input ] > 0);\r\ + \n :return [];\r\ + \n}\r\ + \n\r\ + \n:set HumanReadableNum do={\r\ + \n :local Input [ :tonum \$1 ];\r\ + \n :local Base [ :tonum \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :local Prefix \"kMGTPE\";\r\ + \n :local Pow 1;\r\ + \n :set Base [ \$EitherOr \$Base 1024 ];\r\ + \n :local Bin [ \$IfThenElse (\$Base = 1024) \"i\" \"\" ];\r\ + \n :if (\$Input < \$Base) do={:return \$Input}\r\ + \n :for I from=0 to=[ :len \$Prefix ] do={\r\ + \n :set Pow (\$Pow * \$Base);\r\ + \n :if (\$Input / \$Base < \$Pow) do={\r\ + \n :set Prefix [ :pick \$Prefix \$I ];\r\ + \n :local Tmp1 (\$Input * 100 / \$Pow);\r\ + \n :local Tmp2 (\$Tmp1 / 100);\r\ + \n :if (\$Tmp2 >= 100) do={:return (\$Tmp2 . \$Prefix . \$Bin)}\r\ + \n :return (\$Tmp2 . \".\" . \\\r\ + \n [ :pick \$Tmp1 [ :len \$Tmp2 ] ([ :len \$Tmp1 ] - [ :len \$Tmp\ + 2 ] + 1) ] . \\\r\ + \n \$Prefix . \$Bin);\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set CleanFilePath do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CharacterReplace;\r\ + \n :while (\$Path ~ \"//\") do={ :set \$Path [ \$CharacterReplace \$Path \ + \"//\" \"/\" ]; }\r\ + \n :if ([ :pick \$Path 0 ] = \"/\") do={ :set Path [ :pick \$Path 1 [ :le\ + n \$Path ] ]; }\r\ + \n :if ([ :pick \$Path ([ :len \$Path ] - 1) ] = \"/\") do={ :set Path [ \ + :pick \$Path 0 ([ :len \$Path ] - 1) ]; }\r\ + \n :return \$Path;\r\ + \n}\r\ + \n\r\ + \n:set CleanName do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Return \"\";\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :typeof [ find \"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqr\ + stuvwxyz0123456789\" \$Char ] ] = \"nil\") do={\r\ + \n :do {:if ([ :len \$Return ] = 0) do={:error true};\r\ + \n :if ([ :pick \$Return ([ :len \$Return ] - 1) ] = \"-\") do={:er\ + ror true}; :set Char \"-\"} on-error={:set Char \"\"}; \r\ + \n };\r\ + \n :set Return (\$Return . \$Char)};\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set RandomDelay do={\r\ + \n :local Time [ :tonum \$1 ];\r\ + \n :local Unit [ :tostr \$2 ];\r\ + \n :global EitherOr;\r\ + \n :global GetRandomNumber;\r\ + \n :global MAX;\r\ + \n :if (\$Time = 0) do={:return false}\r\ + \n :delay ([ \$MAX 10 [ \$GetRandomNumber ([ :tonsec [ :totime (\$Time . \ + [ \$EitherOr \$Unit \"s\" ]) ] ] / 1000000) ] ] . \"ms\");\r\ + \n}\r\ + \n\r\ + \n:set EscapeForRegEx do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"^.[]\\\$()|*+\?{}\\\\\");\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :if ([ :find \$Chars \$Char ]) do={:set Char (\"\\\\\" . \$Char)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set ExitOnError do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :local Error [ :tostr \$2 ];\r\ + \n :global IfThenElse;\r\ + \n :log error (\$Name . \" \" . [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\ + \\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with erro\ + r\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \$E\ + rror) \".\" ]);\r\ + \n}\r\ + \n\r\ + \n:set MiniDateTimeStamp do={\r\ + \n :local mikro [ :tobool \$1 ];\r\ + \n /system/clock;\r\ + \n :local vdate [get date];\r\ + \n :local vtime [get time];\r\ + \n :local vdoff [:toarray \"0,4,5,7,8,10\"];\r\ + \n :local MM [:pick \$vdate (\$vdoff->2) (\$vdoff->3)];\r\ + \n :local M [:tonum \$MM];\r\ + \n :if (\$vdate ~ \".../../....\") do={\r\ + \n :set vdoff [:toarray \"7,11,1,3,4,6\"];\r\ + \n :set M ([:find \"xxanebarprayunulugepctovecANEBARPRAYUNULUGE\ + PCTOVEC\" [:pick \$vdate (\$vdoff->2) (\$vdoff->3)] -1] / 2);\r\ + \n :if (\$M>12) do={:set M (\$M - 12)}\r\ + \n :set MM [:pick (100 + \$M) 1 3];\r\ + \n }\r\ + \n :local yyyy [:pick \$vdate (\$vdoff->0) (\$vdoff->1)];\r\ + \n :local dd [:pick \$vdate (\$vdoff->4) (\$vdoff->5)];\r\ + \n :local HH [:pick \$vtime 0 2];\r\ + \n :local mm [:pick \$vtime 3 5];\r\ + \n :local ss [:pick \$vtime 6 8];\r\ + \n :local Return;\r\ + \n :if (\$mikro = true) do={\r\ + \n :set Return ([:pick \$yyyy 2 4] . [:pick \$dd 0 1] . [:pick \$HH 0\ + \_1] . [:pick \$mm 3 4] . [:pick \$ss 6 7]);\r\ + \n } else={\r\ + \n :set Return ([\$yyyy] . [\$MM] . [\$dd] . [\$HH] . [\$mm] . [\$ss]);\ + \r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set WaitForFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :local WaitTime [ :totime \$2 ];\r\ + \n :global CleanFilePath;\r\ + \n :global EitherOr;\r\ + \n :global MAX;\r\ + \n :set FileName [ \$CleanFilePath \$FileName ];\r\ + \n :local Delay ([ \$MAX [ \$EitherOr \$WaitTime 2s ] 100ms ] / 9);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } delay=\$Delay max=10;\r\ + \n } on-error={ }\r\ + \n :while ([ :len [ /file/find where name=\$FileName ] ] > 0) do={\r\ + \n :do {\r\ + \n /file/get \$FileName;\r\ + \n :return true;\r\ + \n } on-error={ }\r\ + \n :delay \$Delay;\r\ + \n :set Delay (\$Delay * 3 / 2);\r\ + \n }\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set FileGet do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global WaitForFile;\r\ + \n :if ([ \$WaitForFile \$FileName 0s ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n :local FileVal false;\r\ + \n :do {\r\ + \n :set FileVal [ /file/get \$FileName ];\r\ + \n } on-error={ }\r\ + \n :return \$FileVal;\r\ + \n}\r\ + \n\r\ + \n:set RmDir do={\r\ + \n :local DirName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing directory: \". \$DirName);\r\ + \n :local DirVal [ \$FileGet \$DirName ];\r\ + \n :if (\$DirVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n };\r\ + \n :if (\$DirVal->\"type\" != \"directory\") do={\r\ + \n :log error (\$0 . \" Directory '\" . \$DirName . \"' is not a direct\ + ory.\");\r\ + \n :return false;\r\ + \n };\r\ + \n :onerror Err {\r\ + \n /file/remove \$DirName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing directory '\" . \$DirName . \"' failed\ + : \" . \$Err);\r\ + \n :return false;\r\ + \n };\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set RmFile do={\r\ + \n :local FileName [ :tostr \$1 ];\r\ + \n :global FileGet;\r\ + \n :log debug (\$0 . \" Removing file: \". \$FileName);\r\ + \n :local FileVal [ \$FileGet \$FileName ];\r\ + \n :if (\$FileVal = false) do={\r\ + \n :log debug (\$0 . \" ... which does not exist.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if (\$FileVal->\"type\" = \"directory\" || \$FileVal->\"type\" = \"di\ + sk\") do={\r\ + \n :log error (\$0 . \" File '\" . \$FileName . \"' is not a file.\");\ + \r\ + \n :return false;\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/remove \$FileName;\r\ + \n } do={\r\ + \n :log error (\$0 . \" Removing file '\" . \$FileName . \"' failed: \"\ + \_. \$Err); :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MkDir do={\r\ + \n :local Path [ :tostr \$1 ];\r\ + \n :global CleanFilePath;\r\ + \n :global FileGet;\r\ + \n :global RmDir;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :local MkTmpfs do={\r\ + \n :global WaitForFile;\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\$0 . \" The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\$0 . \" Creating disk of type tmpfs.\");\r\ + \n \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource\ + /get total-memory ] / 3);\r\ + \n \$WaitForFile \"tmpfs\";\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Creating disk of type tmpfs failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :set Path [ \$CleanFilePath \$Path ];\r\ + \n :if (\$Path = \"\") do={\r\ + \n :return true;\r\ + \n }\r\ + \n :log debug (\$0 . \" Making directory: \" . \$Path);\r\ + \n :local PathVal [ \$FileGet \$Path ];\r\ + \n :if (\$PathVal->\"type\" = \"directory\") do={\r\ + \n :log debug (\$0 . \" ... which already exists.\");\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ :pick \$Path 0 5 ] = \"tmpfs\") do={\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :return false;\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=\$Path;\r\ + \n \$WaitForFile \$Path;\r\ + \n } do={\r\ + \n :log warning (\$0 . \" Making directory '\" . \$Path . \"' failed: \ + \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set MAX do={:if (\$1 > \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n:set MIN do={:if (\$1 < \$2) do={:return \$1}\r\ + \n :return \$2;\r\ + \n}\r\ + \n\r\ + \n:set CharacterMultiply do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Num [ :tonum \$2 ];\r\ + \n :if (\$Num = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :for I from=1 to=\$Num do={:set Return (\$Return . \$Str)}\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set CharacterReplace do={\r\ + \n :local String [ :tostr \$1 ];\r\ + \n :local ReplaceFrom [ :tostr \$2 ];\r\ + \n :local ReplaceWith [ :tostr \$3 ];\r\ + \n :local Return \"\";\r\ + \n :if (\$ReplaceFrom = \"\") do={:return \$String}\r\ + \n :while ([ :typeof [ :find \$String \$ReplaceFrom ] ] != \"nil\") do={\ + \r\ + \n :local Pos [ :find \$String \$ReplaceFrom ];\r\ + \n :set Return (\$Return . [ :pick \$String 0 \$Pos ] . \$ReplaceWith);\ + \r\ + \n :set String [ :pick \$String (\$Pos + [ :len \$ReplaceFrom ]) [ :len\ + \_\$String ] ];\r\ + \n }\r\ + \n :return (\$Return . \$String);\r\ + \n}\r\ + \n\r\ + \n:set IfThenElse do={\r\ + \n :if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] = true) do={:return \ + \$2};\r\ + \n :return \$3;\r\ + \n}\r\ + \n\r\ + \n:set EitherOr do={\r\ + \n :global IfThenElse;\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:return [ \$IfThenElse (\$1 != 0) \ + \$1 \$2 ]}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:return [ \$IfThenElse (\$1 > 0s)\ + \_\$1 \$2 ]}\r\ + \n# this works for boolean values, literal ones with parentheses\r\ + \n :return [ \$IfThenElse ([ :len [ :tostr \$1 ] ] > 0) \$1 \$2 ];\r\ + \n}\r\ + \n\r\ + \n:set ParseKeyValueStore do={\r\ + \n :local Source \$1;\r\ + \n :if ([ :pick \$Source 0 1 ] = \"{\") do={\r\ + \n :do {\r\ + \n :return [ :deserialize from=json \$Source ];\r\ + \n } on-error={ }\r\ + \n } \r\ + \n :if ([ :typeof \$Source ] != \"array\") do={:set Source [ :tostr \$1 ]\ + }\r\ + \n :local Result ({});\r\ + \n :foreach KeyValue in=[ :toarray \$Source ] do={\r\ + \n :if ([ :find \$KeyValue \"=\" ]) do={\r\ + \n :local Key [ :pick \$KeyValue 0 [ :find \$KeyValue \"=\" ] ];\r\ + \n :local Value [ :pick \$KeyValue ([ :find \$KeyValue \"=\" ] + 1) [\ + \_:len \$KeyValue ] ];\r\ + \n :if (\$Value=\"true\") do={:set Value true}\r\ + \n :if (\$Value=\"false\") do={:set Value false}\r\ + \n :set (\$Result->\$Key) \$Value;\r\ + \n } else={:set (\$Result->\$KeyValue) true}\r\ + \n }\r\ + \n :return \$Result;\r\ + \n}\r\ + \n:set RequiredRouterOS do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :local Required [ :tostr \$2 ];\r\ + \n :local Warn [ :tostr \$3 ];\r\ + \n :global IfThenElse;\r\ + \n :global VersionToNum;\r\ + \n :if (!(\$Required ~ \"^\\\\d+\\\\.\\\\d+((alpha|beta|rc|\\\\.)\\\\d+|)\ + \\\$\")) do={\r\ + \n :log error (\$0 . \" No valid RouterOS version: \" . \$Required);\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$VersionToNum \$Required ] > [ \$VersionToNum [ /system/packag\ + e/update/get installed-version ] ]) do={\r\ + \n :if (\$Warn = \"true\") do={\r\ + \n :log warning (\$0 . \" This \" . [ \$IfThenElse ([ :pick \$Caller \ + 0 ] = (\"\\\$\")) \"function\" \"script\" ] . \\\r\ + \n \" '\" . \$Caller . \"' (at least specific functionality) requir\ + es RouterOS \" . \$Required . \". Please update!\");\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set ValidateSyntax do={\r\ + \n :local Code [ :tostr \$1 ];\r\ + \n :global LogPrint;\r\ + \n :onerror Err {\r\ + \n [ :parse (\":local Validate do={\\n\" . \$Code . \"\\n}\") ];\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Valdation failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set VersionToNum do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :local Multi 0x1000000;\r\ + \n :local Return 0;\r\ + \n :global CharacterReplace;\r\ + \n :set Input [ \$CharacterReplace \$Input \".\" \",\" ];\r\ + \n :foreach I in={ \"zero\"; \"alpha\"; \"beta\"; \"rc\" } do={\r\ + \n :set Input [ \$CharacterReplace \$Input \$I (\",\" . \$I . \",\") ];\ + \r\ + \n }\r\ + \n :foreach Value in=([ :toarray \$Input ], 0) do={\r\ + \n :local Num [ :tonum \$Value ];\r\ + \n :if (\$Multi = 0x100) do={\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={\r\ + \n :set Return (\$Return + 0xff00);\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n } else={\r\ + \n :if (\$Value = \"zero\") do={ }\r\ + \n :if (\$Value = \"alpha\") do={ :set Return (\$Return + 0x3f00); \ + }\r\ + \n :if (\$Value = \"beta\") do={ :set Return (\$Return + 0x5f00); }\ + \r\ + \n :if (\$Value = \"rc\") do={ :set Return (\$Return + 0x7f00); }\r\ + \n }\r\ + \n }\r\ + \n :if ([ :typeof \$Num ] = \"num\") do={ :set Return (\$Return + (\$Va\ + lue * \$Multi)); }\r\ + \n :set Multi (\$Multi / 0x100);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set ScriptLock do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local WaitMax [ :totime \$2 ];\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global ScriptLockOrder;\r\ + \n :if ([ :typeof \$ScriptLockOrder ] = \"nothing\") do={\r\ + \n :set ScriptLockOrder ({});\r\ + \n }\r\ + \n :if ([ :typeof (\$ScriptLockOrder->\$Script) ] = \"nothing\") do={\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :local JobCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :return [ :len [ /system/script/job/find where script=\$Script ] ];\ + \r\ + \n }\r\ + \n :local TicketCount do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :local Count 0;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:set Count (\$Count \ + + 1)}\r\ + \n }\r\ + \n :return \$Count;\r\ + \n }\r\ + \n :local IsFirstTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Check [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if (\$Ticket = \$Check) do={:return true}\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\" && \$Ticket != \$Check) d\ + o={:return false}\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :local AddTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Add [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :while (true) do={\r\ + \n :local Pos [ :len (\$ScriptLockOrder->\$Script) ];\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Pos) \$Add;\r\ + \n :delay 10ms;\r\ + \n :if ((\$ScriptLockOrder->\$Script->\$Pos) = \$Add) do={:return tru\ + e}\r\ + \n }\r\ + \n }\r\ + \n :local RemoveTicket do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :local Remove [ :tostr \$2 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Id,Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :while ((\$ScriptLockOrder->\$Script->\$Id) = \$Remove) do={\r\ + \n :set (\$ScriptLockOrder->\$Script->\$Id);\r\ + \n :delay 10ms;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :local CleanupTickets do={\r\ + \n :local Script [ :tostr \$1 ];\r\ + \n :global ScriptLockOrder;\r\ + \n :foreach Ticket in=(\$ScriptLockOrder->\$Script) do={\r\ + \n :if ([ :typeof \$Ticket ] != \"nothing\") do={:return false}\r\ + \n }\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n }\r\ + \n :if ([ :typeof \$WaitMax ] = \"nil\" ) do={:set WaitMax 0s}\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\r\ + \n :log error (\$0 . \" A script named '\" . \$Script . \"' does not ex\ + ist!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$JobCount \$Script ] = 0) do={\r\ + \n \$LogPrint error \$0 (\"No script '\" . \$Script . \"' is running!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$TicketCount \$Script ] >= [ \$JobCount \$Script ]) do={\r\ + \n :log error (\$0 . \" More tickets than running scripts '\" . \$Scrip\ + t . \"', resetting!\");\r\ + \n :set (\$ScriptLockOrder->\$Script) ({});\r\ + \n /system/script/job/remove [ find where script=\$Script ];\r\ + \n }\r\ + \n :local MyTicket [ \$GetRandom20CharAlNum 6 ];\r\ + \n \$AddTicket \$Script \$MyTicket;\r\ + \n :local WaitInterval (\$WaitMax / 20);\r\ + \n :local WaitTime \$WaitMax;\r\ + \n :while (\$WaitTime > 0 && \\\r\ + \n ([ \$IsFirstTicket \$Script \$MyTicket ] = false || \\\r\ + \n [ \$TicketCount \$Script ] < [ \$JobCount \$Script ])) do={\r\ + \n :set WaitTime (\$WaitTime - \$WaitInterval);\r\ + \n :delay \$WaitInterval;\r\ + \n }\r\ + \n :if ([ \$IsFirstTicket \$Script \$MyTicket ] = true && \\\r\ + \n [ \$TicketCount \$Script ] = [ \$JobCount \$Script ]) do={\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$CleanupTickets \$Script;\r\ + \n :return true;\r\ + \n }\r\ + \n \$RemoveTicket \$Script \$MyTicket;\r\ + \n \$LogPrint debug \$0 (\"Script '\" . \$Script . \"' started more than \ + once\" . \\\r\ + \n [ \$IfThenElse (\$WaitTime < \$WaitMax) \" and timed out waiting for\ + \_lock\" \"\" ] . \"...\");\r\ + \n :return false;\r\ + \n}\r\ + \n\r\ + \n:set SendNotification do={ :onerror Err {\r\ + \n :global SendNotification2;\r\ + \n \$SendNotification2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3;\ + \_silent=\$4 });\r\ + \n} do={:global ExitOnError; \$ExitOnError \$0 \$Err}\r\ + \n}\r\ + \n\r\ + \n:set SendNotification2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n :foreach FunctionName,Discard in=\$NotificationFunctions do={\r\ + \n (\$NotificationFunctions->\$FunctionName) (\"\\\$NotificationFunctio\ + ns->\\\"\" . \$FunctionName . \"\\\"\") \$Notification;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:set SymbolByUnicodeName do={\r\ + \n :local Name [ :tostr \$1 ];\r\ + \n :global EitherOr;\r\ + \n :global SymbolsExtra;\r\ + \n :local Symbols ({\r\ + \n \"abacus\"=\"\\F0\\9F\\A7\\AE\";\r\ + \n \"alarm-clock\"=\"\\E2\\8F\\B0\";\r\ + \n \"arrow-down\"=\"\\E2\\AC\\87\";\r\ + \n \"arrow-up\"=\"\\E2\\AC\\86\";\r\ + \n \"calendar\"=\"\\F0\\9F\\93\\85\";\r\ + \n \"card-file-box\"=\"\\F0\\9F\\97\\83\";\r\ + \n \"chart-decreasing\"=\"\\F0\\9F\\93\\89\";\r\ + \n \"chart-increasing\"=\"\\F0\\9F\\93\\88\";\r\ + \n \"cloud\"=\"\\E2\\98\\81\";\r\ + \n \"cross-mark\"=\"\\E2\\9D\\8C\";\r\ + \n \"earth\"=\"\\F0\\9F\\8C\\8D\";\r\ + \n \"fire\"=\"\\F0\\9F\\94\\A5\";\r\ + \n \"floppy-disk\"=\"\\F0\\9F\\92\\BE\";\r\ + \n \"gear\"=\"\\E2\\9A\\99\";\r\ + \n \"heart\"=\"\\E2\\99\\A5\";\r\ + \n \"high-voltage-sign\"=\"\\E2\\9A\\A1\";\r\ + \n \"incoming-envelope\"=\"\\F0\\9F\\93\\A8\";\r\ + \n \"information\"=\"\\E2\\84\\B9\";\r\ + \n \"large-orange-circle\"=\"\\F0\\9F\\9F\\A0\";\r\ + \n \"large-red-circle\"=\"\\F0\\9F\\94\\B4\";\r\ + \n \"link\"=\"\\F0\\9F\\94\\97\";\r\ + \n \"lock-with-ink-pen\"=\"\\F0\\9F\\94\\8F\";\r\ + \n \"memo\"=\"\\F0\\9F\\93\\9D\";\r\ + \n \"mobile-phone\"=\"\\F0\\9F\\93\\B1\";\r\ + \n \"pushpin\"=\"\\F0\\9F\\93\\8C\";\r\ + \n \"scissors\"=\"\\E2\\9C\\82\";\r\ + \n \"scroll\"=\"\\F0\\9F\\93\\9C\";\r\ + \n \"smiley-partying-face\"=\"\\F0\\9F\\A5\\B3\";\r\ + \n \"smiley-smiling-face\"=\"\\E2\\98\\BA\";\r\ + \n \"smiley-winking-face-with-tongue\"=\"\\F0\\9F\\98\\9C\";\r\ + \n \"sparkles\"=\"\\E2\\9C\\A8\";\r\ + \n \"speech-balloon\"=\"\\F0\\9F\\92\\AC\";\r\ + \n \"star\"=\"\\E2\\AD\\90\";\r\ + \n \"warning-sign\"=\"\\E2\\9A\\A0\";\r\ + \n \"white-heavy-check-mark\"=\"\\E2\\9C\\85\"\r\ + \n }, \$SymbolsExtra);\r\ + \n :local Magic [ :pick [ /system/clock/get date ] 4 10 ];\r\ + \n :local Special {\r\ + \n \"information-04-01\"=\"\\F0\\9F\\9A\\BB\";\r\ + \n \"large-orange-circle-04-01\"=\"\\F0\\9F\\8D\\8A\";\r\ + \n \"large-orange-circle-10-31\"=\"\\F0\\9F\\8E\\83\";\r\ + \n \"large-red-circle-04-01\"=\"\\F0\\9F\\8D\\92\" };\r\ + \n :if ([ :len (\$Symbols->\$Name) ] = 0) do={\r\ + \n :log warning (\$0 . \"No symbol available for name '\" . \$Name . \"\ + '!\");\r\ + \n :return \"\";\r\ + \n }\r\ + \n :return ([ \$EitherOr (\$Special->(\$Name . \$Magic)) (\$Symbols->\$Na\ + me) ] . \"\\EF\\B8\\8F\");\r\ + \n}\r\ + \n\r\ + \n:set SymbolForNotification do={\r\ + \n :global NotificationsWithSymbols;\r\ + \n :global SymbolByUnicodeName;\r\ + \n :global IfThenElse;\r\ + \n :if (\$NotificationsWithSymbols != true) do={:return [ \$IfThenElse ([\ + \_:len \$2 ] > 0) ([ :tostr \$2 ] . \" \") \"\" ]}\r\ + \n :local Return \"\";\r\ + \n :foreach Symbol in=[ :toarray \$1 ] do={:set Return (\$Return . [ \$Sy\ + mbolByUnicodeName \$Symbol ])}\r\ + \n :return (\$Return . \" \");\r\ + \n}\r\ + \n\r\ + \n:set Unix2Dos do={:return [ :tocrlf [ :tostr \$1 ] ]}\r\ + \n:set UrlEncode do={\r\ + \n :local Input [ :tostr \$1 ];\r\ + \n :if ([ :len \$Input ] = 0) do={:return \"\"}\r\ + \n :local Return \"\";\r\ + \n :local Chars (\"\\n\\r !\\\"#\\\$%&'()*+,:;<=>\?@[\\\\]^`{|}~\");\r\ + \n :local Subs { \"%0A\"; \"%0D\"; \"%20\"; \"%21\"; \"%22\"; \"%23\"; \"\ + %24\"; \"%25\"; \"%26\"; \"%27\";\r\ + \n \"%28\"; \"%29\"; \"%2A\"; \"%2B\"; \"%2C\"; \"%3A\"; \"%3B\"; \ + \"%3C\"; \"%3D\"; \"%3E\"; \"%3F\";\r\ + \n \"%40\"; \"%5B\"; \"%5C\"; \"%5D\"; \"%5E\"; \"%60\"; \"%7B\"; \ + \"%7C\"; \"%7D\"; \"%7E\" };\r\ + \n :for I from=0 to=([ :len \$Input ] - 1) do={\r\ + \n :local Char [ :pick \$Input \$I ];\r\ + \n :local Replace [ :find \$Chars \$Char ];\r\ + \n :if ([ :typeof \$Replace ] = \"num\") do={:set Char (\$Subs->\$Repla\ + ce)}\r\ + \n :set Return (\$Return . \$Char);\r\ + \n }\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set IsDNSResolving do={\r\ + \n :do {:resolve \"api.telegram.org\"} on-error={:return false}\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsFullyConnected do={\r\ + \n :global IsDNSResolving;\r\ + \n :global IsTimeSync;\r\ + \n :if ([ \$IsDNSResolving ] = false) do={:return false};\r\ + \n :if ([ \$IsTimeSync ] = false) do={:return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set IsTimeSync do={\r\ + \n :global IsTimeSyncCached;\r\ + \n :global IsTimeSyncResetNtp;\r\ + \n :if (\$IsTimeSyncCached = true) do={:return true}\r\ + \n :if ([ /system/ntp/client/get enabled ] = true) do={\r\ + \n :if ([ /system/ntp/client/get status ] = \"synchronized\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :if ([ :typeof \$IsTimeSyncResetNtp ] = \"nothing\") do={\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n }\r\ + \n :if (\$Uptime - \$IsTimeSyncResetNtp < 3m) do={:return false}\r\ + \n :log warning \"\$0 The ntp client is configured, but did not sync.\"\ + ;\r\ + \n :set IsTimeSyncResetNtp \$Uptime;\r\ + \n /system/ntp/client/set enabled=no;\r\ + \n :delay 20ms;\r\ + \n /system/ntp/client/set enabled=yes;\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ /system/license/get ]->\"level\" = \"free\" || \\\r\ + \n [ /system/resource/get ]->\"board-name\" = \"x86\") do={\r\ + \n :log debug \"\$0 No ntp client configured, relying on RTC for CHR fr\ + ee license and x86.\";\r\ + \n :return true;\r\ + \n }\r\ + \n :if ([ /ip/cloud/get update-time ] = true) do={\r\ + \n :if ([ :typeof [ /ip/cloud/get public-address ] ] = \"ip\") do={\r\ + \n :set IsTimeSyncCached true;\r\ + \n :return true;\r\ + \n }\r\ + \n :return false;\r\ + \n }\r\ + \n :log debug \"\$0 No time source configured! Returning gracefully...\";\ + \r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:set FetchHugeFiles do={\r\ + \n :local ScriptName [ :tostr \$1 ];\r\ + \n :local Url [ :tostr \$2 ];\r\ + \n :local CheckCert [ :tostr \$3 ];\r\ + \n\r\ + \n :global CleanName;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global MkDir;\r\ + \n :global RmDir;\r\ + \n :global RmFile;\r\ + \n :global WaitForFile;\r\ + \n\r\ + \n :set CheckCert [ \$IfThenElse (\$CheckCert = \"false\") \"no\" \"yes-w\ + ithout-crl\" ];\r\ + \n\r\ + \n :local DirName (\"tmpfs/\" . [ \$CleanName \$ScriptName ]);\r\ + \n :if ([ \$MkDir \$DirName ] = false) do={\r\ + \n \$LogPrint error \$0 (\"Failed creating directory!\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileName (\$DirName . \"/\" . [ \$CleanName \$0 ] . \"-\" . [ \ + \$GetRandom20CharAlNum ]);\r\ + \n :onerror Err {\r\ + \n /tool/fetch check-certificate=\$CheckCert \$Url dst-path=\$FileName \ + \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$ScriptName ] }) as-va\ + lue;\r\ + \n } do={\r\ + \n :if ([ \$WaitForFile \$FileName 500ms ] = true) do={\r\ + \n \$RmFile \$FileName;\r\ + \n }\r\ + \n \$LogPrint debug \$0 (\"Failed downloading from \" . \$Url . \" - \"\ + \_. \$Err);\r\ + \n \$RmDir \$DirName;\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$WaitForFile \$FileName 5s ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"The file downloaded from \" . \$Url . \" did\ + \_not show up.\");\r\ + \n :return false;\r\ + \n }\r\ + \n\r\ + \n :local FileSize [ /file/get \$FileName size ];\r\ + \n :local Return \"\";\r\ + \n :local VarSize 0;\r\ + \n :while (\$VarSize != \$FileSize) do={\r\ + \n :set Return (\$Return . ([ /file/read offset=\$VarSize chunk-size=32\ + 768 file=\$FileName as-value ]->\"data\"));\r\ + \n :set FileSize [ /file/get \$FileName size ];\r\ + \n :set VarSize [ :len \$Return ];\r\ + \n :if (\$VarSize > \$FileSize) do={\r\ + \n :delay 100ms;\r\ + \n }\r\ + \n }\r\ + \n \$RmDir \$DirName;\r\ + \n :return \$Return;\r\ + \n}\r\ + \n\r\ + \n:set FetchUserAgentStr do={\r\ + \n :local Caller [ :tostr \$1 ];\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global CommitBrief;\r\ + \n :global IfThenElse;\r\ + \n :local Resource [ /system/resource/get ];\r\ + \n :return (\"User-Agent: Mikrotik/\" . \$Resource->\"version\" . \" \" .\ + \_\$Resource->\"architecture-name\" . \\\r\ + \n \" \" . \$Caller . \"/Fetch (\" . \$GlobalScriptsUrl . \"; \" . [ \$\ + CommitBrief ] . \")\");\r\ + \n}\r\ + \n\r\ + \n:set ScriptInstallUpdate do={ :onerror Err {\r\ + \n :local Scripts [ :toarray \$1 ];\r\ + \n :local NewComment [ :tostr \$2 ];\r\ + \n :global InitSetup;\r\ + \n\r\ + \n :global CommitId;\r\ + \n :global ScriptUpdatesCRLF;\r\ + \n :global GlobalConfVersion;\r\ + \n :global Identity;\r\ + \n :global CertificateAvailable;\r\ + \n :global CheckSumsVerify;\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SumsDirAndFileName;\r\ + \n :global GlobalScriptsUrlSuffix;\r\ + \n :global EitherOr;\r\ + \n :global FetchUserAgentStr;\r\ + \n :global Grep;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ParseKeyValueStore;\r\ + \n :global RequiredRouterOS;\r\ + \n :global SendNotification2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global CommentsInScripts;\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Root YE\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, trying wit\ + hout.\");\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :if ([ :len [ /system/script/find where name=\$Script ] ] = 0) do={\ + \r\ + \n \$LogPrint info \$0 (\"Adding new script: \" . \$Script);\r\ + \n /system/script/add name=\$Script owner=\$Script source=\"#!rsc by \ + Vados\\n\" comment=\$NewComment;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local CommitIdBefore \$CommitId;\r\ + \n :local GlobalConfVersionBefore \$GlobalConfVersion;\r\ + \n :local ReloadGlobal false;\r\ + \n :local DeviceMode [ /system/device-mode/get ];\r\ + \n\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSums ({});\r\ + \n :do {\r\ + \n :local Url (\$GlobalScriptsUrl . \$SumsDirAndFileName . \$GlobalScr\ + iptsUrlSuffix);\r\ + \n \$LogPrint debug \$0 (\"Fetching checksums from url: \" . \$Url);\ + \r\ + \n :set CheckSums [ :deserialize from=json ([ /tool/fetch check-certi\ + ficate=yes-without-crl \\\r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url output\ + =user as-value ]->\"data\") ];\r\ + \n } on-error={ \$LogPrint warning \$0 (\"Err = \" . \$Err); }\r\ + \n }\r\ + \n\r\ + \n :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vado\ + s\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"comment\") \ + ];\r\ + \n :local SourceNew;\r\ + \n\r\ + \n :foreach Scheduler in=[ /system/scheduler/find where on-event~(\"\\\ + \\b\" . \$ScriptVal->\"name\" . \"\\\\b\") ] do={\r\ + \n :local SchedulerVal [ /system/scheduler/get \$Scheduler ];\r\ + \n :if (\$ScriptVal->\"policy\" != \$SchedulerVal->\"policy\") do={\r\ + \n \$LogPrint warning \$0 (\"Policies differ for script '\" . \$Scr\ + iptVal->\"name\" . \"' and its scheduler '\" . \$SchedulerVal->\"name\" . \ + \"'!\");\r\ + \n }\r\ + \n }\r\ + \n :do {\r\ + \n :if (\$ScriptInfo->\"ignore\" = true) do={\r\ + \n \$LogPrint debug \$0 (\"Ignoring script '\" . \$ScriptVal->\"nam\ + e\" . \"', as requested.\");\r\ + \n :error true;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :local CheckSum (\$CheckSums->(\$ScriptVal->\"name\"));\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Scri\ + ptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :convert transform=md5 to=hex [ :tolf (\$ScriptVal->\"sou\ + rce\") ] ] = \$CheckSum) do={\r\ + \n \$LogPrint debug \$0 (\"Checksum for script '\" . \$ScriptVal-\ + >\"name\" . \"' matches, ignoring.\");\r\ + \n :error true;\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len (\$ScriptInfo->\"certificate\") ] > 0) do={\r\ + \n :if ([ \$CertificateAvailable (\$ScriptInfo->\"certificate\") \"\ + fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading certificate failed, tryi\ + ng without.\");\r\ + \n }\r\ + \n }\r\ + \n :onerror Err {\r\ + \n :local BaseUrl [ \$EitherOr (\$ScriptInfo->\"base-url\") \$Globa\ + lScriptsUrl ];\r\ + \n :local UrlSuffix [ \$EitherOr (\$ScriptInfo->\"url-suffix\") \$G\ + lobalScriptsUrlSuffix ];\r\ + \n :local Url (\$BaseUrl . \$ScriptVal->\"name\" . \".rsc\" . \$Url\ + Suffix);\r\ + \n \$LogPrint warning \$0 (\"Fetching script '\" . (\$ScriptVal->\"\ + name\") . \"' from url: \" . \$Url);\r\ + \n :local Result [ /tool/fetch check-certificate=yes-without-crl \\\ + \r\ + \n http-header-field=({ [ \$FetchUserAgentStr \$0 ] }) \$Url outp\ + ut=user as-value ];\r\ + \n :if (\$Result->\"status\" = \"finished\") do={\r\ + \n :set SourceNew [ :tolf (\$Result->\"data\") ];\r\ + \n } \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Failed fetching script '\" . \$ScriptV\ + al->\"name\" . \"': \" . \$Err);\r\ + \n :if (\$Err != \"Fetch failed with status 404\") do={\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$ScriptVal->\"source\" = \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Removing dummy. Typo on installation\ + \?\");\r\ + \n /system/script/remove \$Script;\r\ + \n :error false;\r\ + \n }\r\ + \n :if (\$CheckSumsVerify = true) do={\r\ + \n :if ([ :len (\$ScriptInfo->\"base-url\") ] = 0 && [ :len (\$Sc\ + riptInfo->\"url-suffix\") ] = 0 && \\\r\ + \n [ :len \$CheckSum ] = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Added the script manually\? Skip u\ + pdates with 'ignore=true' in comment.\");\r\ + \n }\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$SourceNew ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"No update for script '\" . (\$ScriptVal-\ + >\"name\") . \"'.\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local SourceCRLF [ :tocrlf \$SourceNew ];\r\ + \n :if (\$InitSetup != true && ((\$ScriptVal->\"name\") != \"AM-Globa\ + lConfig\" || (\$ScriptVal->\"name\") != \"AM-GlobalFunc\")) do={\r\ + \n :if (\$SourceNew = (\$ScriptVal->\"source\") || \$SourceCRLF = (\ + \$ScriptVal->\"source\")) do={\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\") \ + . \"' source did not change. End of lines set to CRLF.\");\r\ + \n if (\$ScriptUpdatesCRLF = false) do={\r\ + \n :put [ :tolf \$SourceNew ];\r\ + \n \$LogPrint debug \$0 (\"Script '\" . (\$ScriptVal->\"name\"\ + ) . \"' source update end of lines to LF.\");\r\ + \n }\r\ + \n :error false;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if ([ :pick \$SourceNew 0 14 ] != \"#!rsc by Vados\") do={\r\ + \n \$LogPrint warning \$0 (\"Looks like new script '\" . \$ScriptVa\ + l->\"name\" . \"' is not valid (missing shebang = '\$shebang') Ignoring!\"\ + );\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if (\$CommentsInScripts = true) do={\r\ + \n :local CommentLine [ \$Grep \$SourceNew (\"\\23 Script comment:\ + \_\") ];\r\ + \n :if ([ :len \$CommentLine ] = 0) do={\r\ + \n \$LogPrint debug \$0 (\"The search in new script '\" . (\$Scr\ + iptVal->\"name\") . \"' for comment in script body, is not found result Ig\ + noring!\");\r\ + \n :error false;\r\ + \n } else={\r\ + \n :if ([ :len (\$ScriptVal->\"comment\")] = 0 || [ :len (\$Scri\ + ptVal->\"comment\")] != [ :pick \$CommentLine ([ :find \$CommentLine \":\"\ + ] + 2) [ :len \$CommentLine] ]) do={\r\ + \n \$LogPrint debug \$0 (\"Exsiting comment for script '\" . (\ + \$ScriptVal->\"name\") . \"' is '\" . (\$ScriptVal->\"comment\") . \"'.\")\ + ;\r\ + \n :set (\$ScriptVal->\"comment\") [ :pick \$CommentLine ([ :f\ + ind \$CommentLine \":\"] + 2) [ :len \$CommentLine] ];\r\ + \n \$LogPrint debug \$0 (\"New comment (\" . (\$ScriptVal->\"c\ + omment\") . \") for script '\" . (\$ScriptVal->\"name\") . \"' has been ad\ + ded!\");\r\ + \n } else={\r\ + \n \$LogPrint debug \$0 (\"Comment for script '\" . (\$ScriptV\ + al->\"name\") . \"' did not change.\");\r\ + \n :error false;\r\ + \n }\r\ + \n } \r\ + \n }\r\ + \n\r\ + \n :local RequiredROS ([ \$ParseKeyValueStore [ \$Grep \$SourceNew (\ + \"\\23 requires RouterOS, \") ] ]->version);\r\ + \n :if ([ \$RequiredRouterOS \$0 [ \$EitherOr \$RequiredROS \"0.0\" ]\ + \_false ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires RouterOS \" . \\\r\ + \n \$RequiredROS . \", which is not met by your installation. I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n :local RequiredDM [ \$ParseKeyValueStore [ \$Grep \$SourceNew (\"\ + \\23 requires device-mode, \") ] ];\r\ + \n :local MissingDM ({});\r\ + \n :foreach Feature,Value in=\$RequiredDM do={\r\ + \n :if ([ :typeof (\$DeviceMode->\$Feature) ] = \"bool\" && (\$Devi\ + ceMode->\$Feature) = false) do={\r\ + \n :set MissingDM (\$MissingDM, \$Feature);\r\ + \n }\r\ + \n }\r\ + \n :if ([ :len \$MissingDM ] > 0) do={\r\ + \n \$LogPrint warning \$0 (\"The script '\" . (\$ScriptVal->\"name\ + \") . \"' requires disabled \" . \\\r\ + \n \"device-mode features (\" . [ :tostr \$MissingDM ] . \"). I\ + gnoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$ValidateSyntax \$SourceNew ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Syntax validation for script '\" . (\$\ + ScriptVal->\"name\") . \"' failed! Ignoring!\");\r\ + \n :error false;\r\ + \n }\r\ + \n \r\ + \n :log info (\"Updating script: \" . (\$ScriptVal->\"name\") . \" Sc\ + ript comment: \" . (\$ScriptVal->\"comment\"));\r\ + \n\r\ + \n /system/script/set owner=(\$ScriptVal->\"name\") comment=(\$Script\ + Val->\"comment\") \\\r\ + \n source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRL\ + F \$SourceNew ] \$Script;\r\ + \n :if (\$ScriptVal->\"name\" = \"AM-GlobalConfig\" || \\\r\ + \n \$ScriptVal->\"name\" = \"AM-GlobalFunc\" || \\\r\ + \n \$ScriptVal->\"name\" ~ (\"^(AM-GlobalFunc\\\\.d|mod)/.\")) d\ + o={\r\ + \n :set ReloadGlobal true;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n :if (\$ReloadGlobal = true) do={\r\ + \n \$LogPrint info \$0 (\"Reloading global configuration and functions.\ + \");\r\ + \n :onerror Err {\r\ + \n :global InitSetup (a);\r\ + \n /system/script {run AM-GlobalConfig; run AM-GlobalFunc;};\r\ + \n } do={\$LogPrint error \$0 (\"Reloading global configuration and fun\ + ctions failed! \" . \$Err)}\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore > \$GlobalConfVersion) do={\r\ + \n \$LogPrint warning \$0 (\"The configuration version decreased from \ + \" . \\\r\ + \n \$GlobalConfVersionBefore . \" to \" . \$GlobalConfVersion . \\\r\ + \n \". Installed an older version\?\");\r\ + \n }\r\ + \n :if (\$GlobalConfVersionBefore < \$GlobalConfVersion) do={\r\ + \n :global GlobalConfChanges;\r\ + \n :global GlobalConfMigration;\r\ + \n :local ChangeLogCode;\r\ + \n :if ([ :len \$ChangeLogCode ] > 0) do={\r\ + \n :if ([ \$ValidateSyntax \$ChangeLogCode ] = true) do={\r\ + \n :onerror Err {\r\ + \n [ :parse \$ChangeLogCode ];\r\ + \n } do={\$LogPrint warning \$0 (\"The changelog failed to run: \" \ + . \$Err)}\r\ + \n } else={\$LogPrint warning \$0 (\"The changelog failed syntax vali\ + dation!\")}\r\ + \n }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Migration (\$GlobalConfMigration->[ :tostr \$I ]);\r\ + \n :do {\r\ + \n :if ([ :typeof \$Migration ] != \"str\") do={\r\ + \n \$LogPrint debug \$0 (\"Migration code for change \" . \$I .\ + \_\" is not available.\");\r\ + \n :error false;\r\ + \n }\r\ + \n :if ([ \$ValidateSyntax \$Migration ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Migration code for change \" . \$I\ + \_. \" failed syntax validation!\");\r\ + \n :error false}\r\ + \n \$LogPrint info \$0 (\"Applying migration for change \" . \$I \ + . \": \" . \$Migration);\r\ + \n :onerror Err {\r\ + \n [ :parse \$Migration ];\r\ + \n } do={\$LogPrint warning \$0 (\"Migration code for change \" .\ + \_\$I . \" failed to run: \" . \$Err)}\r\ + \n } on-error={ }\r\ + \n }\r\ + \n }\r\ + \n :local NotificationMessage (\"The configuration version on \" . \$Id\ + entity . \" increased \" . \\\r\ + \n \"to \" . \$GlobalConfVersion . \", current configuration may nee\ + d modification. \" . \\\r\ + \n \"Please review and update global-config-overlay, then re-run glo\ + bal-config.\");\r\ + \n \$LogPrint info \$0 (\$NotificationMessage);\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nChanges:\ + \");\r\ + \n :for I from=(\$GlobalConfVersionBefore + 1) to=\$GlobalConfVersion\ + \_do={\r\ + \n :local Change (\$GlobalConfChanges->[ :tostr \$I ]);\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n \" . \\\ + \r\ + \n [ \$SymbolForNotification \"pushpin\" \"*\" ] . \$Change);\r\ + \n \$LogPrint info \$0 (\"Change \" . \$I . \": \" . \$Change);\r\ + \n }\r\ + \n } else={\r\ + \n :set NotificationMessage (\$NotificationMessage . \"\\n\\nNews and\ + \_changes are not available.\");\r\ + \n\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$GlobalConfChanges ] > 0) do={ :set GlobalConfChanges; \ + } else={ :global GlobalConfChanges (a); }\r\ + \n :if ([ :len \$GlobalConfMigration ] > 0) do={ :set GlobalConfMigrati\ + on; } else={ :global GlobalConfMigration (a); }\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n\r\ + \n# load modules\r\ + \n:foreach Script in=[ /system/script/find where name ~ \"^(global-functio\ + ns\\\\.d|mod)/.\" ] do={\r\ + \n :local ScriptVal [ /system/script/get \$Script ];\r\ + \n :if ([ \$ValidateSyntax (\$ScriptVal->\"source\") ] = true) do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' f\ + ailed to run: \" . \$Err);\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint error \$0 (\"Module '\" . \$ScriptVal->\"name\" . \"' fai\ + led syntax validation, skipping.\");\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# Add and fix global scheduler\r\ + \n:put \"Add or/and fix Global scheduler...\";\r\ + \n:local OnEvent \"/system/script { run AM-GlobalConfig; run AM-GlobalFunc\ + ; }\";\r\ + \n:local Comment \"Every Day Set Global Environment Variables\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-SetGlobalEnv\" ] ] \ + = 0) do={\r\ + \n /system/scheduler/add name=\"AM-SetGlobalEnv\" comment=\$Comment on-ev\ + ent=\$OnEvent start-time=startup;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" comment ] != \$Comment)\ + \_do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" comment=\$Comment;\r\ + \n}\r\ + \n:if ([ /system/scheduler/get \"AM-SetGlobalEnv\" on-event ] != \$OnEvent\ + ) do={\r\ + \n /system/scheduler/set \"AM-SetGlobalEnv\" on-event=\$OnEvent;\r\ + \n}\r\ + \n# Add Update Script scheduler\r\ + \n:put \"Add or/and fix Update Script scheduler\";\r\ + \n:set OnEvent \":global ScriptInstallUpdate; \\\$ScriptInstallUpdate;\";\ + \r\ + \n:set Comment \"Every Day Check For Update Scripts\";\r\ + \n:if ([ :len [ /system/scheduler/find where name=\"AM-ScriptInstallUpdate\ + \" ] ] = 0) do={\r\ + \n/system/scheduler/add name=\"AM-ScriptInstallUpdate\" comment=\$Comment \ + start-time=startup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady;\r\ + \n :retry { :if (\$GlobalConfReady != true) do={ :error (\"Global configs\ + \_not ready.\"); }; } delay=100ms max=5;\r\ + \n\r\ + \n:log warning (\"This script \$ScriptName Loaded \" . \\\r\ + \n [ \$IfThenElse (\$CommitId != \"unknown\") ([ \$CommitBrief ] . \" \ + \") ] . \\\r\ + \n \"on \" . \$devBoardName . \"\\nRouterOS: \" . \$devCurVersion . \"\ + \\nArchitecture: \" . \$devCurArch . \"\\nIdentity: \" . \$Identity);\r\ + \n # signal we are ready\r\ + \n:set GlobalFuncReady true;\r\ + \n:set ExitOK true;\r\ + \n\r\ + \n:log info (\$0 . \"Global configs ready!\");\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n}\r\ + \n" +add comment=\ + "Lookup IP addresses for RKN list and add them to firewall address-list" \ + dont-require-permissions=no name=AM-RKN-dnsLookup owner=AM-RKN-dnsLookup \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-RKN-dnsLookup\r\ + \n# Script comment: Lookup IP addresses for RKN list and add them to firew\ + all address-list\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Script description: This script is intended to be run periodically, e.\ + g. once a day, to update the IP addresses in the firewall address-list bas\ + ed on the RKN list.\r\ + \n# It resolves the domain names in the RKN list and a\ + dds the corresponding IP addresses to the specified firewall address-list.\ + \r\ + \n# The script also handles CNAME records by following\ + \_them until it finds A records.\r\ + \n# Usage:\r\ + \n# /system/script { run AM-RKN-dnsLookup; }\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:global dnsLookupReady false;\r\ + \n:if ([:typeof \$dnsLookupReady] != \"boolean\") do={ :set dnsLookupReady\ + \_true; }\r\ + \nwhile (!\$dnsLookupReady) do={ :nothing; }\r\ + \n:set dnsLookupReady false;\r\ + \n\r\ + \n:local arrRKN {\r\ + \n \"telegra.ph\"\r\ + \n \"grani.ru\"\r\ + \n \"xhamster.com\"\r\ + \n \"lostfilm.tv\"\r\ + \n \"rutracker.org\"\r\ + \n \"lurkmore.to\"\r\ + \n \"dou.ua\"\r\ + \n \"youporn.com\"\r\ + \n \"pornolab.net\"\r\ + \n \"crimerussia.com\"\r\ + \n \"telegram.org\"\r\ + \n \"api.telegram.org\"\r\ + \n \"facebook.com\"\r\ + \n \"instagram.com\"\r\ + \n \"x.com\"\r\ + \n \"twitter.com\"\r\ + \n \"youtube.com\"\r\ + \n \"googleapis.com\"\r\ + \n \"youtu.be\"\r\ + \n \"googlevideo.com\"\r\ + \n \"gstatic.com\"\r\ + \n \"googleusercontent.com\"\r\ + \n \"nhacmp3youtube.com\"\r\ + \n \"telegram.me\"\r\ + \n \"t.me\";}\r\ + \n\r\ + \n:local ListRKN \"RKN-dnsLookup\";\r\ + \n:local ListComment (\"ListRKN managed by \" . \$ScriptName);\r\ + \n:foreach aListItem in=[/ip/firewall/address-list find list=\$ListRKN] do\ + ={\r\ + \n /ip firewall address-list remove \$aListItem;\r\ + \n}\r\ + \n:foreach aItem in=\$arrRKN do={\r\ + \n# :do {\r\ + \n :retry delay=500ms max=5 on-error={:log error \"dns resolve fail\ + ure [ \$aItem - resolve ip ]\"} command={:resolve \$aItem}\r\ + \n# } on-error={\r\ + \n# :log error \"dns resolve failure [ \$arItem - resolve ip ]\";\r\ + \n# }\r\ + \n :foreach dnsRecord in=[/ip dns cache all find where (name=\$aItem)] \ + do={\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"A\") do={\r\ + \n :local newAddress [/ip dns cache all get \$dnsRecord data];\ + \r\ + \n :do {\r\ + \n /ip firewall address-list add list=\$ListRKN address=\$newA\ + ddress comment=\$aItem;\r\ + \n :log warning \"Successfully adding ip to list A record: \ + \$dnsRecord, address: \$newAddress domain: \$aItem\";\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list A record: \$dn\ + sRecord, address: \$newAddress domain: \$aItem\";\r\ + \n }\r\ + \n }\r\ + \n :if ([/ip dns cache all get \$dnsRecord type]=\"CNAME\") do={\r\ + \n :local cname;\r\ + \n :local nextCname;\r\ + \n :local cnameAddress;\r\ + \n :set cname [/ip dns cache all find where (name=\$aItem && ty\ + pe=\"CNAME\")];\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip dns \ + cache all get \$cname data] && type=\"CNAME\")];\r\ + \n :while (\$nextCname != \"\") do={\r\ + \n :set cname \$nextCname;\r\ + \n :set nextCname [/ip dns cache all find where (name=[/ip \ + dns cache all get \$cname data] && type=\"CNAME\")];\r\ + \n }\r\ + \n :foreach aRecord in=[/ip dns cache all find where (name=[/ip\ + \_dns cache all get \$cname data] && type=\"A\")] do={\r\ + \n :do {\r\ + \n :set cnameAddress [/ip dns cache all get \$aRecord d\ + ata];\r\ + \n /ip firewall address-list add list=\$ListRKN address\ + =\$cnameAddress comment=\$aItem;\r\ + \n :log warning \"Succesfully adding ip to list CNAME r\ + ecord: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \ + \$aItem.\";\r\ + \n# /ip firewall raw add action=add-dst-to-address-list\ + \_address-list=\$ListRKN address-list-timeout=none-dynamic chain=preroutin\ + g comment=\$aItem content=\$aItem;\r\ + \n } on-error={\r\ + \n :log error \"error when adding ip to list CNAME reco\ + rd: \$cname, A record: \$aRecord, address: \$newAddress, from domain: \$aI\ + tem.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n :log error (\$ScriptName . \"System Scheduler for script: \" . \$Scri\ + ptName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add name=\$ScriptName on-event=\"/system/script { \ + run \$ScriptName; }\" comment=\"Update RKN DNS Looup\" interval=00:30:00 p\ + olicy=\"ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon\"\ + \_start-time=startup;\r\ + \n }\r\ + \n:set dnsLookupReady true;\r\ + \ndelay 2s; \r\ + \n:global dnsLookupReady (a);\r\ + \n" +add dont-require-permissions=no name="renameLists " owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local oldName \"Test\";\ + \n:local newName \"Test-IP\";\ + \n/ip firewall address-list\ + \n:foreach i in=[find list=\$oldName] do={\ + \nset \$i list=\$newName;\ + \n}\ + \n" +add dont-require-permissions=no name=IPv6-Settings owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + #############Script Settings##################\ + \n\ + \n:local \"IF-TUN\" \"6to4-tun\"\ + \n:local \"IF-LOC\" \"Bridge-LAN\"\ + \n:local \"POOL-6TO4\" \"poolV6-local\"\ + \n:local \"force-update\" false\ + \n############ use then static ip #######################\ + \n:local \"lease-address\" [/ip address get [find interface=\$\"IF-LOC\"] \ + address]\ + \n#:local \"interface\" \"ext\"\ + \n:local bound 0\ + \n\ + \n###############################################\ + \n\ + \n:local 6to4prefix do={\ + \n :global dec2hex\ + \n :local ipv4 \$1\ + \n :local ipv6 \"2002:\"\ + \n :for i from=0 to=2 step=1 do={ \ + \n :if ( \$i = 2) do={\ + \n :set ipv6 (\$ipv6.\":\")\ + \n }\ + \n :local fp [:find \$ipv4 \".\"]\ + \n :if ( \$fp > 0 ) do={\ + \n :set ipv6 (\$ipv6.[\$dec2hex [:pick \$ipv4 0 \$fp]])\ + \n :set ipv4 [:pick \$ipv4 (\$fp + 1 ) [:len \$ipv4]]\ + \n }\ + \n }\ + \n :return (\$ipv6.[\$dec2hex \$ipv4])\ + \n}\ + \n:local \"str_replace\" do={\ + \n :local \"str-pos\" [:find \$1 \$2]\ + \n :local \"str-len\" [:len \$2]\ + \n :local str \"\"\ + \n :local rest \$1\ + \n :while (\$\"str-pos\" >= 0) do={\ + \n :local t [:pick \$rest 0 \$\"str-pos\"]\ + \n :if ( \$4 > 0 ) do={\ + \n :for i from=1 to=(\$4 - [:len \$t]) step=1 do={\ + \n :set str (\$str.\$5)\ + \n }\ + \n }\ + \n :set str (\$str.\$t.\$3)\ + \n :set rest [:pick \$rest (\$\"str-pos\"+\$\"str-len\") [:len \$re\ + st]]\ + \n :set \"str-pos\" [:find \$rest \$2]\ + \n }\ + \n :return (\$str.\$rest)\ + \n}\ + \n\ + \n:if (\$bound=1) do={\ + \n :local t [:find \$\"lease-address\" \"/\"]\ + \n\ + \n :if ( \$t >= 0 ) do={\ + \n :set \"lease-address\" [:pick \$\"lease-address\" 0 \$t]\ + \n }\ + \n\ + \n :if ( [/interface 6to4 print count-only where name=\$\"IF-TUN\"] = 0\ + ) do={\ + \n /interface 6to4 add !keepalive local-address=\$\"lease-address\"\ + \_mtu=1280 name=\$\"IF-TUN\" remote-address=10.30.30.0\ + \n :set \"force-update\" true\ + \n }\ + \n\ + \n :local \"IPv4-TUN\" [/interface 6to4 get [find name=\$\"IF-TUN\"] lo\ + cal-address];\ + \n\ + \n :if ( \$\"lease-address\" != \$\"IPv4-TUN\" || \$\"force-update\" = \ + true) do={\ + \n /interface 6to4 set [find name=\$\"IF-TUN\"] local-address=\$\"l\ + ease-address\"\ + \n /ipv6 address remove [find interface=\$\"IF-TUN\"]\ + \n /ipv6 address remove [find interface=\$\"IF-LOC\"]\ + \n /ipv6 route remove [find gateway ~ \$\"IF-TUN\" and dst-address=\ + \"::/0\"]\ + \n :local \"pool-prefix\" [\$6to4prefix \$\"lease-address\"]\ + \n :if ( [/ipv6 pool print count-only where name=\$\"POOL-6TO4\"] =\ + \_0) do={ \ + \n /ipv6 pool add name=\$\"POOL-6TO4\" prefix=(\$\"pool-prefix\ + \".\":1::/64\") prefix-length=64\ + \n } else={\ + \n /ipv6 pool set [/ipv6 pool find where name=\$\"POOL-6TO4\"] \ + prefix=(\$\"pool-prefix\".\":1::/64\")\ + \n }\ + \n /ipv6 address add address=(\$\"pool-prefix\".\"::\".[\$MAC2EUI64\ + \_[/interface get [find name=\$\"interface\"] mac-address]].\"/3\") interf\ + ace=\$\"IF-TUN\" advertise=no\ + \n /ipv6 address add eui-64=yes from-pool=\$\"POOL-6TO4\" interface\ + =\$\"IF-LOC\" advertise=yes\ + \n /ipv6 route add dst-address=::/0 gateway=(\"::10.30.30.81%\".\$\ + \"IF-TUN\")\ + \n :local t [/ipv6 address get [find interface=\$\"IF-LOC\"] addres\ + s]\ + \n :set t [:pick \$t 0 [:find \$t \"/\"]]\ + \n :if ( [/ipv6 dhcp-server option print count-only where name=DNS]\ + \_= 0) do={\ + \n /ipv6 dhcp-server option add code=23 name=DNS value=(\"0x\".\ + [\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n } else={\ + \n /ipv6 dhcp-server option set [/ipv6 dhcp-server option find \ + where name=DNS] value=(\"0x\".[\$\"str_replace\" \$t \":\" \"\" 4 \"0\"])\ + \n }\ + \n :if ( [/ipv6 dhcp-server print count-only where interface=\$\"IF\ + -LOC\"] = 0) do={\ + \n /ipv6 dhcp-server add address-pool=\$\"POOL-6TO4\" dhcp-opti\ + on=DNS interface=\$\"IF-LOC\" lease-time=1d name=\$\"IF-LOC\"\ + \n }\ + \n :if ( [ipv6 nd print count-only where interface=\$\"IF-LOC\"] = \ + 0) do={\ + \n /ipv6 nd add advertise-dns=no interface=\$\"IF-LOC\" other-c\ + onfiguration=yes\ + \n }\ + \n }\ + \n}" +add dont-require-permissions=no name=IPv6-GlobalFunc owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global MAC2EUI64 do={\ + \n :local EUI64 \"\"\ + \n :local MAC \$1\ + \n :local HEX2DEC \"0123456789ABCDEF\"\ + \n :for i from=0 to=5 step=1 do={\ + \n :if ( \$i = 0 ) do={\ + \n :set EUI64 [:pick \$MAC 0]\ + \n :set EUI64 (\$EUI64.[:tostr [:pick \$\"HEX2DEC\" ([:find \$\ + \"HEX2DEC\" [:pick \$MAC 1]] ^ 2)]])\ + \n }\ + \n :if ( \$i = 1 || \$i = 3) do={\ + \n :set EUI64 (\$EUI64.\":\")\ + \n }\ + \n :if ( \$i = 2 ) do={\ + \n :set EUI64 (\$EUI64.\"FF:FE\")\ + \n }\ + \n :if ( [:find \$MAC \":\"] = 2 ) do={\ + \n :set MAC [:pick \$MAC 3 [:len \$MAC]]\ + \n :set EUI64 (\$EUI64.[:pick \$MAC 0 2])\ + \n }\ + \n }\ + \n :return (\$EUI64)\ + \n}\ + \n\ + \n:global dec2hex do={\ + \n :local hex \"\"\ + \n :local dec [:tonum \$1]\ + \n :for i from=0 to=4 step=4 do={\ + \n :set hex ([:pick \"0123456789ABCDEF\" ((\$dec>>\$i)&0xf) (((\$de\ + c>>\$i)&0xf)+1)].\$hex)\ + \n }\ + \n :return ([:tostr \$hex])\ + \n}" +/user group +add name=backup policy="ssh,read,write,policy,test,web,sniff,sensitive,api,res\ + t-api,!local,!telnet,!ftp,!reboot,!winbox,!password,!romon" +/ip smb +set enabled=no interfaces=lo +/interface bridge port +add bridge=Bridge-LAN interface=ether5 +add bridge=Bridge-LAN interface=ether2 +add bridge=Bridge-LAN interface=ether4 trusted=yes +add bridge=Bridge-LAN interface=ether3 +add bridge=Bridge-LAN interface=wlan2 +add bridge=Bridge-LAN interface=wlan1 +/interface bridge settings +set use-ip-firewall=yes +/ip firewall connection tracking +set udp-timeout=10s +/ip neighbor discovery-settings +set discover-interface-list=LAN lldp-med=no lldp-poe-power=no +/ip settings +set accept-redirects=yes ipv4-multipath-hash-policy=l4 max-neighbor-entries=\ + 8192 +/ipv6 settings +set disable-ipv6=yes disable-link-local-address=yes forward=no \ + max-neighbor-entries=8192 multipath-hash-policy=l4 +/interface detect-internet +set detect-interface-list=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=ether4 list=WAN +add interface="ether1 - Unet" list=Internet +add interface=WG list=WAN +add interface=Bridge-LAN list=LAN +/interface wireguard peers +add allowed-address=0.0.0.0/0 client-allowed-address=::/0 endpoint-address=\ + 109.107.177.79 endpoint-port=29185 interface=WG name=VDSina \ + persistent-keepalive=10s public-key=\ + "zuRsO+dL/j6awaBWZ/NgnHR+bCNUjRWbaHFpdzkNnw4=" +/interface wireless cap +# +set bridge=Bridge-LAN caps-man-addresses=10.30.30.10 \ + caps-man-certificate-common-names=CAPsMAN-51B25E507D07 certificate=\ + CAP-488F5A8E1ABC discovery-interfaces=Bridge-LAN enabled=yes interfaces=\ + wlan2,wlan1 lock-to-caps-man=yes +/ip address +add address=10.30.30.81/24 interface=Bridge-LAN network=10.30.30.0 +add address=10.8.0.2/24 interface=WG network=10.8.0.0 +/ip cloud +set update-time=no +/ip dhcp-client +add add-default-route=special-classless default-route-distance=2 \ + default-route-tables=main:1 dhcp-options=clientid,clientid_duid \ + interface="ether1 - Unet" name=client1 use-peer-dns=no use-peer-ntp=no +/ip dhcp-server config +set accounting=no +/ip dhcp-server lease +add address=10.30.30.101 client-id=1:48:8f:5a:8e:ee:a8 mac-address=\ + 48:8F:5A:8E:EE:A8 server=masterDHCP +add address=10.30.30.107 client-id=1:f0:de:f1:91:d:1e mac-address=\ + F0:DE:F1:91:0D:1E server=masterDHCP +add address=10.30.30.83 client-id=1:0:80:77:ec:43:86 mac-address=\ + 00:80:77:EC:43:86 server=masterDHCP +add address=10.30.30.70 dhcp-option=domain-search-list,HostName \ + dhcp-option-set=HP1505n mac-address=00:1E:0B:1A:88:8D server=masterDHCP +add address=10.30.30.50 client-id=1:4:7c:16:36:f5:37 mac-address=\ + 04:7C:16:36:F5:37 server=masterDHCP +add address=10.30.30.71 client-id=1:10:2d:41:2c:20:c2 mac-address=\ + 10:2D:41:2C:20:C2 server=masterDHCP +add address=10.30.30.61 client-id=1:10:8:c1:81:59:5f mac-address=\ + 10:08:C1:81:59:5F server=masterDHCP +add address=10.30.30.72 client-id=1:10:38:1f:d2:5f:13 mac-address=\ + 10:38:1F:D2:5F:13 server=masterDHCP +add address=10.30.30.99 client-id=1:10:b1:df:e9:d0:d5 mac-address=\ + 10:B1:DF:E9:D0:D5 server=masterDHCP +add address=10.30.30.111 client-id=1:fe:2:52:be:3f:16 mac-address=\ + FE:02:52:BE:3F:16 server=masterDHCP +add address=10.30.30.92 client-id=1:d4:6d:6d:fd:e5:49 mac-address=\ + D4:6D:6D:FD:E5:49 server=masterDHCP +add address=10.30.30.93 client-id=1:7e:8b:1e:cb:b8:cb mac-address=\ + CC:42:10:12:46:94 server=masterDHCP +add address=10.30.30.94 client-id=1:50:cc:f8:85:96:a mac-address=\ + 50:CC:F8:85:96:0A server=masterDHCP +add address=10.30.30.82 client-id=1:e4:18:6b:58:34:ab mac-address=\ + E4:18:6B:58:34:AB server=masterDHCP +add address=10.30.30.75 client-id=1:0:27:15:41:97:a9 mac-address=\ + 00:27:15:41:97:A9 server=masterDHCP +add address=10.30.30.77 dhcp-option=HostName dhcp-option-set=StartLife1 \ + mac-address=C4:82:E1:03:49:10 server=masterDHCP +add address=10.30.30.73 client-id=1:10:38:1f:82:81:f2 mac-address=\ + 10:38:1F:82:81:F2 server=masterDHCP +add address=10.30.30.95 client-id=1:88:ae:dd:60:39:54 dhcp-option=HostName \ + dhcp-option-set=VADOS-NUC mac-address=88:AE:DD:60:39:54 server=masterDHCP +add address=10.30.30.98 client-id=1:c8:5b:76:d8:cc:bf dhcp-option=HostName \ + dhcp-option-set=VADOS-LEN mac-address=C8:5B:76:D8:CC:BF server=masterDHCP +add address=10.30.30.96 client-id=iPhone mac-address=4A:91:ED:0E:1F:39 \ + server=masterDHCP +add address=10.30.30.74 client-id=MiTV dhcp-option=HostName dhcp-option-set=\ + MiTV mac-address=20:72:A9:D3:34:36 server=masterDHCP +add address=10.30.30.30 dhcp-option=HostName dhcp-option-set=miller \ + mac-address=F0:75:5D:1E:5F:71 server=masterDHCP +add address=10.30.30.100 client-id=iVados dhcp-option=HostName \ + dhcp-option-set=iVados mac-address=DE:4D:72:F3:1A:9F server=masterDHCP +add address=10.30.30.10 allow-dual-stack-queue=yes client-id=\ + ff:0:0:0:df:0:3:0:1:f0:75:5d:1e:5f:85 dhcp-option=HostName \ + dhcp-option-set=MikroNUC mac-address=F0:75:5D:1E:5F:85 server=masterDHCP \ + use-src-mac=yes +/ip dhcp-server network +add address=10.30.30.0/24 caps-manager=10.30.30.10 dhcp-option=\ + domain-search-list dhcp-option-set=domain-search-set dns-server=\ + 10.30.30.33,109.107.177.79 domain=WORKGROUP gateway=10.30.30.81 netmask=\ + 24 ntp-server=10.30.30.81,10.30.30.10 wins-server=10.30.30.95,10.30.30.98 +/ip dns +set allow-remote-requests=yes cache-max-ttl=1d doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,8.8.8.8 \ + verify-doh-cert=yes +/ip dns static +add address=109.107.177.79 comment="VDSina Server DNS" name=ns1.vados.ru \ + type=A +add address=109.107.177.79 name=vds.vados.ru type=A +add comment="Local DNS server for vados.ru" disabled=yes ns=10.30.30.81 \ + regexp=*.vados.ru type=NS +add comment="*.vados.ru Regexp" disabled=yes forward-to=10.30.30.81 regexp=\ + "\".*\\\\.vados\\\\.ru\"" type=FWD +add comment="*.30.30.10.in-addr.arpa Regexp" disabled=yes forward-to=\ + 10.30.30.81 regexp="\".*\\\\.30\\\\.30\\\\.10\\\\.in-addr\\\\.arpa" type=\ + FWD +add address=10.30.30.81 comment=NS.vados.ru.local disabled=yes name=\ + ns.vados.ru.local type=A +add comment="DNS ns.vados.ru.local" disabled=yes match-subdomain=yes name=\ + vados.ru ns=ns.vados.ru.local type=NS +add address=10.30.30.33 comment=reg.vados.ru name=reg.vados.ru type=A +add address=8.8.8.8 comment="Google-dns IPv4 1" match-subdomain=yes name=\ + dns.google type=A +add address=8.8.4.4 comment="Google-dns IPv4 2" name=dns.google type=A +add address=10.30.30.33 comment=AMSTER name=amster.vados.ru type=A +add address=10.30.30.33 name=ros.vados.ru type=A +add address=10.30.30.95 name=vados-nuc type=A +add address=10.30.30.33 name=amster-nuc type=A +add address=10.30.30.33 name=amster type=A +add address=10.30.30.33 name=amster-reg type=A +add address=10.30.30.33 disabled=yes name=mng.vados.ru type=A +add address=10.30.30.33 name=mgr.vados.ru type=A +add address=10.30.30.33 name=prt.vados.ru type=A +add address=10.30.30.33 disabled=yes match-subdomain=yes name=\ + mikrots.vados.ru type=A +add address=10.30.30.30 comment=MILLER name=miller.vados.ru type=A +add address=10.30.30.30 name=MILLER-LEN type=A +add address=10.30.30.30 name=miller type=A +add address=1.0.0.1 comment="CloudFlare-doh IPv4 2" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=1.1.1.1 comment="CloudFlare-doh IPv4 1" disabled=yes name=\ + security.cloudflare-dns.com type=A +add address=9.9.9.9 comment="Cuad9-dns IPv4 1" disabled=yes name=\ + dns.quad9.net type=A +add address=149.112.112.112 comment="Cuad9-dns IPv4 2" disabled=yes name=\ + dns.quad9.net type=A +add address=195.133.25.16 comment="DNS Comss" disabled=yes name=dns.comss.one \ + type=A +add address=10.30.30.81 comment=LocalNet name=MikroVados type=A +add address=10.30.30.10 name=MikroNUC type=A +add address=10.30.30.86 disabled=yes name=MikroYellow type=A +add address=10.30.30.85 disabled=yes name=MikroMic type=A +add address=10.30.30.95 name=VADOS-NUC type=A +add address=10.30.30.98 name=VADOS-LEN type=A +add address=91.192.189.2 comment=Unet-DNS disabled=yes name=ns1.unet.ws type=\ + A +add address=10.30.30.20 disabled=yes name=garage type=A +/ip firewall address-list +add address=91.215.120.206 list=TRUSTED +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=8.8.4.0/24 list=YouTube +add address=8.8.8.0/24 list=YouTube +add address=8.34.208.0/20 list=YouTube +add address=8.35.192.0/20 list=YouTube +add address=23.236.48.0/20 list=YouTube +add address=23.251.128.0/19 list=YouTube +add address=34.0.0.0/15 list=YouTube +add address=34.2.0.0/16 list=YouTube +add address=34.3.0.0/23 list=YouTube +add address=34.3.3.0/24 list=YouTube +add address=34.3.4.0/24 list=YouTube +add address=34.3.8.0/21 list=YouTube +add address=34.3.16.0/20 list=YouTube +add address=34.3.32.0/19 list=YouTube +add address=34.3.64.0/18 list=YouTube +add address=34.4.0.0/14 list=YouTube +add address=34.8.0.0/13 list=YouTube +add address=34.16.0.0/12 list=YouTube +add address=34.32.0.0/11 list=YouTube +add address=34.64.0.0/10 list=YouTube +add address=34.128.0.0/10 list=YouTube +add address=35.184.0.0/13 list=YouTube +add address=35.192.0.0/14 list=YouTube +add address=35.196.0.0/15 list=YouTube +add address=35.198.0.0/16 list=YouTube +add address=35.199.0.0/17 list=YouTube +add address=35.199.128.0/18 list=YouTube +add address=35.200.0.0/13 list=YouTube +add address=35.208.0.0/12 list=YouTube +add address=35.224.0.0/12 list=YouTube +add address=35.240.0.0/13 list=YouTube +add address=57.140.192.0/18 list=YouTube +add address=64.15.112.0/20 list=YouTube +add address=64.233.160.0/19 list=YouTube +add address=66.22.228.0/23 list=YouTube +add address=66.102.0.0/20 list=YouTube +add address=66.249.64.0/19 list=YouTube +add address=70.32.128.0/19 list=YouTube +add address=72.14.192.0/18 list=YouTube +add address=74.125.0.0/16 list=YouTube +add address=104.154.0.0/15 list=YouTube +add address=104.196.0.0/14 list=YouTube +add address=104.237.160.0/19 list=YouTube +add address=107.167.160.0/19 list=YouTube +add address=107.178.192.0/18 list=YouTube +add address=108.59.80.0/20 list=YouTube +add address=108.170.192.0/18 list=YouTube +add address=108.177.0.0/17 list=YouTube +add address=130.211.0.0/16 list=YouTube +add address=136.22.160.0/20 list=YouTube +add address=136.22.176.0/21 list=YouTube +add address=136.22.184.0/23 list=YouTube +add address=136.22.186.0/24 list=YouTube +add address=142.250.0.0/15 list=YouTube +add address=146.148.0.0/17 list=YouTube +add address=152.65.208.0/22 list=YouTube +add address=152.65.214.0/23 list=YouTube +add address=152.65.218.0/23 list=YouTube +add address=152.65.222.0/23 list=YouTube +add address=152.65.224.0/19 list=YouTube +add address=162.120.128.0/17 list=YouTube +add address=162.216.148.0/22 list=YouTube +add address=162.222.176.0/21 list=YouTube +add address=172.110.32.0/21 list=YouTube +add address=172.217.0.0/16 list=YouTube +add address=172.253.0.0/16 list=YouTube +add address=173.194.0.0/16 list=YouTube +add address=173.255.112.0/20 list=YouTube +add address=192.158.28.0/22 list=YouTube +add address=192.178.0.0/15 list=YouTube +add address=193.186.4.0/24 list=YouTube +add address=199.36.154.0/23 list=YouTube +add address=199.36.156.0/24 list=YouTube +add address=199.192.112.0/22 list=YouTube +add address=199.223.232.0/21 list=YouTube +add address=207.223.160.0/20 list=YouTube +add address=208.65.152.0/22 list=YouTube +add address=208.68.108.0/22 list=YouTube +add address=208.81.188.0/22 list=YouTube +add address=208.117.224.0/19 list=YouTube +add address=209.85.128.0/17 list=YouTube +add address=216.58.192.0/19 list=YouTube +add address=216.73.80.0/20 list=YouTube +add address=216.239.32.0/19 list=YouTube +add address=2ip.ru list=Test-IP +add address=wg.vados.ru comment=VDSIna list=TRUSTED +add address=10.8.0.0/24 comment="WG VDSIna" disabled=yes list=TRUSTED +add address=ikus.pesc.ru comment=ikus.pesc.ru list=NoVdsina +add address=love.ru comment=love.ru list=NoVdsina +add address=gosuslugi.ru comment=gosuslugi.ru list=NoVdsina +add address=pochta.ru comment=pochta.ru list=NoVdsina +add address=vdsina.com list=NoVdsina +add address=vdsina.ru list=NoVdsina +add address=youla.ru list=NoVdsina +add address=kupiprodai.ru list=NoVdsina +add address=myip.ru list=Test-IP +add address=geoip.co list=NoVdsina +add address=comp-lenta.ru list=NoVdsina +add address=128.85.102.70 comment=update.microsoft.com list=\ + windowsupdateslist +add address=wu-b-net.trafficmanager.net. comment=\ + ctldl.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=ctldl.windowsupdate.com.delivery.microsoft.com. comment=\ + ctldl.windowsupdate.com list=windowsupdateslist +add address=redir.update.msft.com.trafficmanager.net. comment=\ + update.microsoft.com list=windowsupdateslist +add address=geo.prod.do.dsp.trafficmanager.net. comment=\ + geo.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=13.74.139.29 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12437.d.akamaiedge.net. comment=\ + kv801.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=displaycatalog-rp.md.mp.microsoft.com.akadns.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=rp-consumer-prod-displaycatalog-geomap.trafficmanager.net. \ + comment=displaycatalog-rp.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=sls.update.microsoft.com. comment=slscr.update.microsoft.com \ + list=windowsupdateslist +add address=glb.sls.prod.dcat.dsp.trafficmanager.net. comment=\ + sls.update.microsoft.com list=windowsupdateslist +add address=fe3.delivery.mp.microsoft.com. comment=\ + fe3cr.delivery.mp.microsoft.com list=windowsupdateslist +add address=glb.cws.prod.dcat.dsp.trafficmanager.net. comment=\ + fe3.delivery.mp.microsoft.com list=windowsupdateslist +add address=fe2cr.update.msft.com.trafficmanager.net. comment=\ + fe2cr.update.microsoft.com list=windowsupdateslist +add address=download.windowsupdate.com.delivery.microsoft.com. comment=\ + download.windowsupdate.com list=windowsupdateslist +add address=wu-f-net.trafficmanager.net. comment=\ + download.windowsupdate.com.delivery.microsoft.com list=windowsupdateslist +add address=tsfe.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=edge.ds-c7110-microsoft.global.dns.qwilted-cds.cqloud.com. \ + comment=9.au.download.windowsupdate.com list=windowsupdateslist +add address=52.158.227.125 comment=array812.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dspw65.akamai.net. comment=\ + download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.151.58.88 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net. \ + comment=licensing.mp.microsoft.com list=windowsupdateslist +add address=licensing-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-licensing-aks2aks.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-nlu-net.trafficmanager.net. comment=\ + star.f.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.b.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-tlu-net.trafficmanager.net. comment=\ + star.b.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=storeedgefd.xbetservices.akadns.net. comment=\ + storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net. \ + comment=storeedgefd.dsx.mp.microsoft.com.edgekey.net list=\ + windowsupdateslist +add address=e16646.dscg.akamaiedge.net. comment=\ + storeedgefd.dsx.mp.microsoft.com.edgekey.net.globalredir.akadns.net list=\ + windowsupdateslist +add address=40.69.76.189 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=bg.microsoft.map.fastly.net. comment=\ + 3.au.download.windowsupdate.com list=windowsupdateslist +add list=windowsupdateslist +add address=40.69.75.33 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sf.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=cdp-f-ssl-tlu-net.trafficmanager.net. comment=\ + star.sf.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a2033.dscd.akamai.net. comment=\ + star.sb.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.138.242 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-nlu-net.trafficmanager.net. comment=\ + dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=1D.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-b-tlu-net.trafficmanager.net. comment=\ + 1d.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a122.dscg3.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=windowsupdateslist +add address=geover.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e10370.d.akamaiedge.net. comment=\ + geover.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=52.232.182.21 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.95 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=a1683.dscd.akamai.net. comment=\ + dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net. \ + comment=collections.mp.microsoft.com list=windowsupdateslist +add address=a1847.dscd.akamai.net. comment=\ + wildcard.f.tlu.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=13.74.142.6 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=au.download.windowsupdate.com.edgesuite.net. comment=\ + 2.au.download.windowsupdate.com list=windowsupdateslist +add address=a767.dscg3.akamai.net. comment=\ + au.download.windowsupdate.com.edgesuite.net list=windowsupdateslist +add address=52.156.123.84 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.78.254 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=4.au.download.windowsupdate.com.delivery.microsoft.com. comment=\ + 4.au.download.windowsupdate.com list=windowsupdateslist +add address=cl-glcb907925.gcdn.co. comment=\ + 4.au.download.windowsupdate.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.111 comment=array510.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.69.70.43 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com. \ + comment=tsfe.trafficshaping.dsp.mp.microsoft.com list=windowsupdateslist +add address=ts-frontend.trafficmanager.net. comment=\ + tsfe.trafficshaping.dsp.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=40.69.75.248 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.74.84 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=sds.trafficmanager.net. comment=software-download.microsoft.com \ + list=windowsupdateslist +add address=40.69.74.80 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.175.242.182 comment=array804.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=dlc-shim.trafficmanager.net. comment=download.microsoft.com list=\ + windowsupdateslist +add address=e12671.dscd.akamaiedge.net. comment=\ + download.microsoft.com.edgekey.net list=windowsupdateslist +add address=20.187.55.34 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.231 comment=array513.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.222.13 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=e12358.d.akamaiedge.net. comment=\ + kv601.prod.do.dsp.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. comment=\ + tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=dcat-f-tlu-net.trafficmanager.net. comment=\ + tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=a1856.dspw65.akamai.net. comment=\ + tlu.dl.delivery.mp.microsoft.com-c.edgesuite.net list=windowsupdateslist +add address=52.167.164.252 comment=array506.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=13.74.137.54 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=kv501.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + kv501.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=collections-prod-frontdoor-geomap.trafficmanager.net. comment=\ + consumer-collections-aks2eap.md.mp.microsoft.com.akadns.net list=\ + windowsupdateslist +add address=52.167.160.173 comment=array509.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.77.148 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.200.126 comment=array507.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=52.167.167.59 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.172 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=smtp.mail.me.com comment=smtp.mail.me.com list=NoVdsina +add address=40.70.76.5 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.186.119 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.177.176.186 comment=array520.prod.do.dsp.mp.microsoft.com \ + list=windowsupdateslist +add address=40.70.76.27 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.160.43 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.110 comment=array817.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.142.8 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.138.254 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.167.96 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.69.76.215 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.143.200 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.111 comment=array818.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.112 comment=array819.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=13.74.137.53 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.98 comment=array805.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.96 comment=array803.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.104 comment=array811.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.101 comment=array808.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.99 comment=array806.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.17 comment=array802.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.97 comment=array804.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.103 comment=array810.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.16 comment=array801.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=40.70.77.177 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.164.84 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.105 comment=array812.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.107 comment=array814.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.232.161.82 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.108 comment=array815.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=52.167.161.91 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.102 comment=array809.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.109 comment=array816.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.106 comment=array813.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.117 comment=array622.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.107 comment=array612.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.108 comment=array613.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.105 comment=array610.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.113 comment=array618.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.116 comment=array621.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.98 comment=array603.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.114 comment=array619.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.111 comment=array616.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.97 comment=array602.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.106 comment=array611.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.110 comment=array615.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.119 comment=array624.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.135 comment=array514.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.97 comment=array503.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.99 comment=array604.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.61 comment=array504.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.131 comment=array510.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.132 comment=array511.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.96 comment=array601.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.104 comment=array609.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.138 comment=array517.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.60 comment=array501.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.139 comment=array518.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.118 comment=array623.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.113 comment=array820.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.103 comment=array608.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.154.7.100 comment=array807.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.129 comment=array508.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.134 comment=array513.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.62 comment=array505.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.137 comment=array516.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.101 comment=array606.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.96 comment=array502.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.130 comment=array509.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.112 comment=array617.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.100 comment=array605.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.115 comment=array620.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.136 comment=array515.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=purchase.md.mp.microsoft.com.akadns.net. comment=\ + purchase.mp.microsoft.com list=windowsupdateslist +add address=e110219.a.akamaiedge.net. comment=\ + purchase.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=72.153.5.140 comment=array519.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.141 list=windowsupdateslist +add address=72.153.5.63 comment=array506.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.128 comment=array507.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.102 comment=array607.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=storesdk.xbetservices.akadns.net. comment=\ + storesdk.dsx.mp.microsoft.com list=windowsupdateslist +add address=e16646.g.akamaiedge.net. comment=\ + storesdk.dsx.mp.microsoft.com.edgekey.net list=windowsupdateslist +add address=software-download.microsoft.com.delivery.microsoft.com. comment=\ + software-download.microsoft.com list=windowsupdateslist +add address=download.prss.microsoft.com.akamaized.net. comment=\ + software-download.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=72.145.35.109 comment=array614.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=72.153.5.133 comment=array512.prod.do.dsp.mp.microsoft.com list=\ + windowsupdateslist +add address=213.180.192.0/19 comment=YandexDisk list=NoVdsina +add address=87.250.250.50 comment=YandexDisk list=NoVdsina +add address=consumer-prod-bcfd-v7-afd.trafficmanager.net. comment=\ + displaycatalog.mp.microsoft.com list=windowsupdateslist +add address=purchase-europeeap.md.mp.microsoft.com.akadns.net. comment=\ + purchase.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=ne-az-conspurchase-mp.trafficmanager.net. comment=\ + purchase-europeeap.md.mp.microsoft.com.akadns.net list=windowsupdateslist +add address=storeedgefd.dsx.mp.microsoft.com.edgekey.net. comment=\ + cdn.storeedgefd.dsx.mp.microsoft.com list=windowsupdateslist +add address=star.f.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.f.tlu.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=a1996.dscd.akamai.net. comment=\ + wildcard.f.dl.delivery.mp.microsoft.com.edgesuite.net list=\ + windowsupdateslist +add address=cp801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=glb.tas02.sls-prod.dcat.dsp.mp.microsoft.com. comment=\ + tas02.sls.update.microsoft.com list=windowsupdateslist +add address=cp601.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + cp601.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=star.sb.tlu.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedgeextensions.sb.tlu.dl.delivery.mp.microsoft.com list=\ + windowsupdateslist +add address=star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com. \ + comment=msedge.sf.dl.delivery.mp.microsoft.com list=windowsupdateslist +add address=cdp-f-ssl-nlu-net.trafficmanager.net. comment=\ + star.sf.dl.delivery.mp.microsoft.com.delivery.microsoft.com list=\ + windowsupdateslist +add address=tm-pifd-prod-gbl-weight.trafficmanager.net. comment=\ + paymentinstruments.mp.microsoft.com list=windowsupdateslist +add address=10.30.30.33 comment=amster disabled=yes list=NoWG +add address=10.30.30.10 comment=MikroNUC disabled=yes list=NoWG +add address=89.110.101.17 comment=!!!VDSINA!!!! disabled=yes list=NoWG +add address=wg.vados.ru comment=VDSina-vpn list=VDSina +add address=80.85.244.9 list=TRUSTED +add address=disc801.prod.do.dsp.mp.microsoft.com.edgekey.net. comment=\ + disc801.prod.do.dsp.mp.microsoft.com list=windowsupdateslist +add address=185.71.70.78 disabled=yes list=TRUSTED +add address=176.53.203.171 disabled=yes list=TRUSTED +add address=176.53.203.170 disabled=yes list=TRUSTED +add address=176.53.202.194 disabled=yes list=TRUSTED +add address=89.110.101.17 comment="!!!VDSina Server!!!" list=NoVdsina +add address=dns.quad9.net comment=Quad9-dns disabled=yes list=Trusted-DNS +add address=dns.google comment=Google-dns list=Trusted-DNS +add address=security.cloudflare-dns.com comment=CloudFlare-dns list=\ + Trusted-DNS +add address=time.google.com comment=Google-time list=Trusted-NTP +add address=0.ru.pool.ntp.org comment=ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=1.ru.pool.ntp.org disabled=yes list=Trusted-NTP +add address=2.ru.pool.ntp.org comment=2.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=3.ru.pool.ntp.org comment=3.ru.pool.ntp.org disabled=yes list=\ + Trusted-NTP +add address=cp.vdsina.com comment=VDSina-vpn list=VDSina +add address=vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns.vados.ru comment=VDSina-vpn list=VDSina +add address=vdsina.com comment=VDSina-vpn list=VDSina +add address=cp.vdsina.ru comment=VDSina-vpn list=VDSina +add address=ns1.vdsina.ru comment=VDSina-vpn list=VDSina +add address=10.30.30.0/24 comment="Local Network" list=Local-Net +add address=10.8.0.0/24 comment="WireGuard Network" list=Local-Net +add address=146.120.64.208/29 comment="External Net" list=Unet +add address=esia.gosuslugi.ru comment=esia.gosuslugi.ru list=NoVdsina +add address=lk.gosuslugi.ru comment=lk.gosuslugi.ru list=NoVdsina +add address=trudvsem.ru comment=trudvsem.ru list=NoVdsina +add address=10.30.30.30 comment=miller disabled=yes list=NoWG +add address=10.30.30.33 comment="Direct connet to DNS servers" list=\ + Direct-DNS +add address=109.107.177.79 list=VPN-Server +add address=10.30.30.10 list=AWG-Proxy +/ip firewall filter +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=12h chain=input comment="block honeypot DNS" \ + connection-state=new dst-port=53 in-interface="ether1 - Unet" protocol=\ + udp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input connection-state=new dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!Trusted-DNS +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment=\ + "block honeypot ssh rdp winbox" connection-state=new dst-port=22,8291 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=!TRUSTED +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot asterisk" \ + connection-state=new dst-port=5060 in-interface="ether1 - Unet" protocol=\ + udp +add action=add-src-to-address-list address-list="Honeypot Hacker" \ + address-list-timeout=4w2d chain=input comment="block honeypot NTP" \ + connection-state=new dst-port=123 in-interface="ether1 - Unet" protocol=\ + udp +add action=fasttrack-connection chain=forward comment=\ + "FastTrack for non-VPN connections" connection-mark=!VPN_No_FastTrack \ + connection-state=established,related,new disabled=yes +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=tcp +add action=accept chain=input disabled=yes dst-port=29185 in-interface=\ + "ether1 - Unet" protocol=udp +add action=accept chain=input comment="tcp Accept input DNS queries from LAN" \ + dst-port=53 protocol=tcp src-address=10.30.30.0/24 +add action=accept chain=input comment="udp Accept input DNS queries" \ + dst-port=53 protocol=udp src-address=10.30.30.0/24 +add action=accept chain=output comment="accept WireGuard" disabled=yes \ + dst-address-list=VPN-Server out-interface="ether1 - Unet" protocol=udp \ + src-port=13231 +add action=accept chain=output disabled=yes dst-address-list=VPN-Server \ + dst-port=29185 out-interface="ether1 - Unet" protocol=udp +add action=fasttrack-connection chain=forward comment="defconf: fasttrack" \ + connection-state=established,related,new disabled=yes routing-mark=\ + !To-VPN +add action=accept chain=output disabled=yes protocol=tcp src-port=443 +add action=accept chain=output disabled=yes src-address=127.0.0.1 +add action=accept chain=forward comment="defconf: accept in ipsec policy" \ + disabled=yes ipsec-policy=in,ipsec +add action=accept chain=forward comment="defconf: accept out ipsec policy" \ + disabled=yes ipsec-policy=out,ipsec +add action=accept chain=forward comment=\ + "defconf: accept established,related, untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid in-interface="ether1 - Unet" src-address=\ + !10.30.30.0/24 +add action=drop chain=forward comment=\ + "defconf: drop all from WAN not DSTNATed" connection-nat-state=!dstnat \ + connection-state=new in-interface="ether1 - Unet" src-address-list=\ + !VDSina +add action=drop chain=forward comment=\ + "Filter Rule to block FB address LIST : )" disabled=yes dst-address-list=\ + facebook +add action=drop chain=forward comment=\ + "Filter Rule to block YOUTUBE address LIST : )" disabled=yes \ + dst-address-list=YouTube +add action=accept chain=input in-interface="ether1 - Unet" protocol=icmp \ + src-address-list=VDSina +add action=accept chain=input connection-state=established disabled=yes +add action=accept chain=input connection-state=related disabled=yes +/ip firewall mangle +add action=mark-routing chain=prerouting comment="Mark Routing main for NoWG" \ + new-routing-mark=main src-address-list=NoWG +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for NoVdsina" dst-address-list=NoVdsina \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for VDSina" dst-address-list=VPN-Server \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "Mark Routing main for MikroNuc" disabled=yes dst-address-list=AWG-Proxy \ + new-routing-mark=main src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=\ + "!!!ERROR Mark Routing main for Unet!!!" disabled=yes dst-address-list=\ + Unet new-routing-mark=main passthrough=no +add action=mark-routing chain=prerouting comment="Test-IP sites" disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=Test-IP new-routing-mark=\ + To-VPN src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment=FuckRKN disabled=yes \ + dst-address=!10.30.30.0/24 dst-address-list=RKN-dnsLookup \ + new-routing-mark=To-VPN passthrough=no src-address=10.30.30.0/24 +add action=mark-routing chain=prerouting comment="AWG for All sites" \ + disabled=yes dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina \ + new-routing-mark=*401 routing-mark=!main src-address=10.30.30.0/24 \ + src-address-list=!NoWG +add action=mark-routing chain=prerouting comment="VDSIna for All sites" \ + dst-address=!10.30.30.0/24 dst-address-list=!NoVdsina new-routing-mark=\ + To-VPN routing-mark=!main src-address=10.30.30.0/24 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 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=redirect chain=dstnat comment="tcp Redirect DNS in NAT" disabled=\ + yes dst-port=53 protocol=tcp src-address=10.30.30.0/24 src-address-list=\ + !NoWG to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/tcp dst-nat to amster.loc. From VDSina Server" dst-port=53 \ + in-interface="ether1 - Unet" protocol=tcp src-address-list=VPN-Server \ + to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat comment=\ + "53/udp dst-nat to amster.loc. From VDSina Server" dst-port=53 protocol=\ + udp src-address-list=VPN-Server to-addresses=10.30.30.33 to-ports=53 +add action=dst-nat chain=dstnat disabled=yes dst-port=44333 in-interface=\ + "ether1 - Unet" protocol=udp src-address=!10.30.30.0/24 to-addresses=\ + 10.30.30.33 to-ports=44333 +add action=masquerade chain=srcnat comment="Default NAT" out-interface=\ + "ether1 - Unet" routing-mark=!To-VPN src-address=10.30.30.0/24 +add action=dst-nat chain=dstnat comment=amster.vados.ruhttps dst-port=443 \ + fragment=no in-interface="ether1 - Unet" protocol=tcp routing-mark=main \ + to-addresses=10.30.30.33 to-ports=443 +add action=dst-nat chain=dstnat comment="amster.vados.ru For Certbot HTTP" \ + dst-port=80 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=80 +add action=dst-nat chain=dstnat comment="amster.vados.ru 3x-ui" disabled=yes \ + dst-port=2904 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2904 +add action=dst-nat chain=dstnat comment=reg.vados.ru disabled=yes dst-port=\ + 5000 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=5000 +add action=dst-nat chain=dstnat comment="Docker swarm" disabled=yes dst-port=\ + 2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=10.30.30.33 \ + to-ports=2377 +add action=dst-nat chain=dstnat comment=npm dst-port=81 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=To-VPN to-addresses=10.30.30.33 \ + to-ports=81 +add action=dst-nat chain=dstnat comment=SSH dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp src-address-list=TRUSTED to-addresses=\ + 10.30.30.33 to-ports=22 +add action=dst-nat chain=dstnat comment="portainer agent" disabled=yes \ + dst-port=2377 in-interface="ether1 - Unet" protocol=tcp to-addresses=\ + 10.30.30.33 to-ports=2377 +add action=dst-nat chain=dstnat disabled=yes dst-port=22 in-interface=\ + "ether1 - Unet" protocol=tcp routing-mark=!To-VPN to-addresses=\ + 10.30.30.33 to-ports=22 +add action=masquerade chain=srcnat comment=WG dst-address-list=!NoVdsina \ + out-interface=WG routing-mark=To-VPN src-address-list=!NoWG +add action=dst-nat chain=dstnat comment="Yandex.DNS Family" disabled=yes \ + dst-port=53 in-interface=Bridge-LAN protocol=udp src-address-list=KIDS \ + time=6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +add action=dst-nat chain=dstnat disabled=yes dst-port=53 in-interface=\ + Bridge-LAN protocol=tcp src-address-list=KIDS time=\ + 6h-15m,sun,mon,tue,wed,thu,fri,sat to-addresses=77.88.8.3 +/ip firewall raw +add action=drop chain=prerouting comment="Drop Honeypot Hackers" \ + in-interface="ether1 - Unet" src-address-list="Honeypot Hacker" +add action=accept chain=prerouting comment="Trusted DNS" disabled=yes \ + in-interface-list=WAN src-address-list=Trusted-DNS +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + extras.getpagespeed.com content=extras.getpagespeed.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=linux.duke.edu \ + content=linux.duke.edu disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtube.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleapis.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ggpht.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=youtu.be \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=ytimg.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googlevideo.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=gstatic.com \ + disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + googleusercontent.com disabled=yes +add action=add-dst-to-address-list address-list=FuckRKN address-list-timeout=\ + none-dynamic chain=prerouting comment="YouTube raw" content=\ + nhacmp3youtube.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=rutube.ru \ + content=rutube.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment="me.com raw" \ + content=me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "icloud.com raw" content=icloud.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com raw" content=smtp.mail.me.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "smtp.mail.me.com.akadns.net raw" content=smtp.mail.me.com.akadns.net \ + disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + "api.db-ip.com raw" content=api.db-ip.com disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=\ + esia.gosuslugi.ru content=esia.gosuslugi.ru disabled=yes +add action=add-dst-to-address-list address-list=NoVdsina \ + address-list-timeout=none-dynamic chain=prerouting comment=gosuslugi.ru \ + content=gosuslugi.ru disabled=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set irc disabled=no +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip ipsec settings +set accounting=no +/ip ipsec policy +set 0 disabled=yes dst-address=0.0.0.0/0 src-address=0.0.0.0/0 +/ip ipsec profile +set [ find default=yes ] dpd-interval=2m dpd-maximum-failures=5 +/ip proxy +set cache-administrator="" max-cache-size=none port=80,443 src-address=\ + 0.0.0.0 +/ip route +add check-gateway=none disabled=no distance=1 dst-address=0.0.0.0/0 gateway=\ + WG routing-table=To-VPN scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set telnet disabled=yes +set reverse-proxy disabled=yes +set ssh address=10.30.30.0/24 port=2222 +set winbox address=10.30.30.0/24 +set api address=10.30.30.0/24 +set api-ssl address=10.30.30.0/24 disabled=yes +set www-ssl certificate=Root-YE port=18443 +set www address=10.30.30.0/24 disabled=yes port=18880 +/ip service webserver +set acme-plain=no crl-plain=no graphs-plain=no graphs-secure=no index-plain=\ + no index-secure=no rest-secure=no scep-plain=no webfig-plain=no \ + webfig-secure=no +/ip ssh +set forwarding-enabled=both publickey-authentication-options=verify-required +/ip upnp +set enabled=yes +/ip upnp interfaces +add interface=Bridge-LAN type=internal +add forced-ip=146.120.64.211 interface="ether1 - Unet" type=external +add forced-ip=109.107.177.79 interface=*E type=external +add interface=lo type=internal +add interface=wlan2 type=internal +add interface=wlan1 type=internal +/ipv6 dhcp-client +add add-default-route=yes disabled=yes interface=*F rapid-commit=no request=\ + info use-interface-duid=yes validate-server-duid=no +/ipv6 dhcp-server option sets +add name=domain-search-set options=domain-search-list +add name="=\"Client ID\"" options=HostName +/ipv6 firewall address-list +add address=::/128 comment="defconf: unspecified address" list=bad_ipv6 +add address=::1/128 comment="defconf: lo" list=bad_ipv6 +add address=fec0::/10 comment="defconf: site-local" list=bad_ipv6 +add address=::ffff:0.0.0.0/96 comment="defconf: ipv4-mapped" list=bad_ipv6 +add address=::/96 comment="defconf: ipv4 compat" list=bad_ipv6 +add address=100::/64 comment="defconf: discard only " list=bad_ipv6 +add address=2001:db8::/32 comment="defconf: documentation" list=bad_ipv6 +add address=2001:10::/28 comment="defconf: ORCHID" list=bad_ipv6 +add address=3ffe::/16 comment="defconf: 6bone" list=bad_ipv6 +/ipv6 firewall filter +add action=accept chain=input comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=input comment="defconf: drop invalid" connection-state=\ + invalid +add action=accept chain=input comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=input comment="defconf: accept UDP traceroute" \ + dst-port=33434-33534 protocol=udp +add action=accept chain=input comment=\ + "defconf: accept DHCPv6-Client prefix delegation." dst-port=546 protocol=\ + udp src-address=fe80::/10 +add action=accept chain=input comment="defconf: accept IKE" dst-port=500,4500 \ + protocol=udp +add action=accept chain=input comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=input comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=input comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=input comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +add action=accept chain=forward comment=\ + "defconf: accept established,related,untracked" connection-state=\ + established,related,untracked +add action=drop chain=forward comment="defconf: drop invalid" \ + connection-state=invalid +add action=drop chain=forward comment=\ + "defconf: drop packets with bad src ipv6" src-address-list=bad_ipv6 +add action=drop chain=forward comment=\ + "defconf: drop packets with bad dst ipv6" dst-address-list=bad_ipv6 +add action=drop chain=forward comment="defconf: rfc4890 drop hop-limit=1" \ + hop-limit=equal:1 protocol=icmpv6 +add action=accept chain=forward comment="defconf: accept ICMPv6" protocol=\ + icmpv6 +add action=accept chain=forward comment="defconf: accept HIP" protocol=139 +add action=accept chain=forward comment="defconf: accept IKE" dst-port=\ + 500,4500 protocol=udp +add action=accept chain=forward comment="defconf: accept ipsec AH" protocol=\ + ipsec-ah +add action=accept chain=forward comment="defconf: accept ipsec ESP" protocol=\ + ipsec-esp +add action=accept chain=forward comment=\ + "defconf: accept all that matches ipsec policy" ipsec-policy=in,ipsec +add action=drop chain=forward comment=\ + "defconf: drop everything else not coming from LAN" in-interface-list=\ + !LAN +/ipv6 nd +set [ find default=yes ] advertise-dns=yes disabled=yes interface=Bridge-LAN \ + managed-address-configuration=yes other-configuration=yes pref64=::/64 +/ipv6 nd prefix +add autonomous=no disabled=yes interface=Bridge-LAN on-link=no prefix=none +/ipv6 nd prefix default +set dhcp6-pd-preferred=yes +/ipv6 nd proxy +add disabled=yes interface=lo +/ipv6 nd settings +set router-advertisement-route-distance=100 +/routing settings +set single-process=no +/system clock +set time-zone-name=Europe/Moscow +/system clock manual +set time-zone=+03:00 +/system identity +set name=MikroMaster +/system logging +add action=email disabled=yes regex="* added|changed|removed *" topics=\ + system,info +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes use-local-clock=yes +/system ntp client servers +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +add address=time.google.com disabled=yes +/system package update +set check-certificate=yes-without-crl ip-version=ipv4 +/system scheduler +add comment="Every Day Set Global Environment Variables" name=AM-SetGlobalEnv \ + on-event="/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" interval=1d name=\ + AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Update RKN DNS Looup" disabled=yes interval=20m name=\ + AM-RKN-dnsLookup on-event="/system/script { run AM-RKN-dnsLookup; }" \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +/system watchdog +set watchdog-timer=no +/tool e-mail +set certificate-verification=no from=MikroVados port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/tool mac-server +set allowed-interface-list=LAN +/tool mac-server mac-winbox +set allowed-interface-list=LAN +/tool netwatch +add certificate=DigiCert-Global-Root-CA check-certificate=no comment="doh, doh\ + -url=https://security.cloudflare-dns.com/dns-query, notify, name=CloudFlar\ + e-doh IPv4 1" disabled=yes down-script="" host=1.1.1.1 http-codes="" \ + interval=10s name="CloudFlare-doh IPv4 1" port=443 test-script="" type=\ + https-get up-script="" +add check-certificate=no comment="doh-fallback, doh-url=https://security.cloud\ + flare-dns.com/dns-query, doh-cert=DigiCert Global Root CA, notify, name=Cl\ + oudFlare-doh IPv4" disabled=yes down-script="" host=1.0.0.1 http-codes="" \ + name="CloudFlare-doh IPv4" port=443 test-script="" type=https-get \ + up-script="" +add comment="dns, notify, name=Google-DNS IPv4" disabled=yes dns-server=\ + 8.8.8.8 down-script="" host=dns.google http-codes="" name=\ + "Google-DNS IPv4" test-script="" type=dns up-script="" +add certificate=GTS-Root-R4 check-certificate=yes comment="doh-fallback, doh-u\ + rl=https://dns.google/dns-query, doh-cert=GTS Root R1, notify, name=Google\ + -DOH IPv4" disabled=yes dns-server=8.8.4.4 down-script="" host=dns.google \ + http-codes="" name="Google-DOH IPv4" port=443 record-type=NS src-address=\ + "" test-script="" type=dns up-script="" +add comment="dns-fallback, notify, name=Cuad9-DOH IPv4" disabled=yes \ + dns-server=9.9.9.9 down-script="" host=dns9.quad9.net http-codes="" \ + interval=10s name="Cuad9-DOH IPv4" src-address=146.120.64.211 \ + test-script="" timeout=800ms type=dns up-script="" +add certificate=DigiCert-Assured-ID-Root-CA comment="doh-fallback, doh-url=htt\ + ps://dns.quad9.net/dns-query, name=Cuad9-DOH IPv4" disabled=yes \ + down-script="" host=149.112.112.112 http-codes="" interval=10s name=\ + "Cuad9-DOH IPv4" port=443 test-script="" type=https-get up-script="" +add comment="dns-fallback, notify, name=ns1.unet.ws" disabled=yes \ + down-script="" host=91.192.189.2 http-codes="" interval=10s name=\ + ns1.unet.ws test-script="" type=simple up-script="" +add check-certificate=yes comment=\ + "doh, doh-url=https://vds.vados.ru/vds-query, name=vds.vados.ru" \ + disabled=yes down-script="" host=109.107.177.79 http-codes="" name=\ + vds.vados.ru port=443 test-script="" type=https-get up-script="" +/user aaa +set accounting=no diff --git a/MikroNUC/20260703/20260703004001-Binary.backup b/MikroNUC/20260703/20260703004001-Binary.backup new file mode 100644 index 0000000000000000000000000000000000000000..de7cb6379475cecc824832048e69c6ce0d853737 GIT binary patch literal 376391 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

D&{ZE#%0mT7SY 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 z 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


HGBWq(#*Id)V1cgsIOs-;79MvF%_GKyGPELR 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

VF=?k5c?`YzvWB*u8>?+y#v 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%0DhE|YPUqK8VPGfz3kb7~|uHFw`A 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

    +RiC_MrmYM;{R^hI{2EsLz37kk$-{ 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¥O%;!Kd1cD{#&ZA9HxsQ-`w7d;TsW?7X zAc*A_(|gG@+iI6wR1@Tn#4bd0fX|GDx>H>qilLl7(Fxn^PZ%#)Z7=0Z&3+AR_oqZu zop&3}Xe5Bf;Dz~VhRsw{PM~gT3Hj;Xg!k^r;};{xOc9aP+PAD*XpvEpY0$016)_*p zrmpGXCbr6baOU1Aw?#77Sg{aCbAi1&HV&Kr9M?GlMkMu#HULZ0Ph7lX?jk|I?8IU? z;FjjrFbKm1;Kdc zeYf}&4_8__a`W2!#9C2&OiLpS3I8LiU?}uEi#Y3v)37`%62B6an{S>SF3~BH* zL0i1$ky>|ZP`n1mIIQO4e=E?>$7S;ooA@Bae=h(!31A<+A-7(cuu4 zbDUpj%Rq$ii+DR{sP{n^D_~j}1~Jg8Y0}GbBsU?5w@~ z0F^r_Bz0u|(vjSqcB+^W$2@qr^JBvTL#ucfCY(kg@QkB#qfG@dTNQO}1#M@~Gq`fATR!fKZ)j8qyO+-p#l6OQKh}#~c9!c2n_#-elO$qD-yE z--C(0k@;2}d(PudYqsT>{a~ZrZq1`5P!+8tllG;MP|N(pW>|T^?8wngKXC?bgy}!v znHb-CcTaCU-aJEEcSFd`!V5-+KZ*Dx!b=_XWG^(? zV>lLQm-|LmP_EG8chEChL3_>sW?JChs3T=-5;ae)5MN(PguM6w-jz28{6_YH z=O^`{J17h4CO6jWPJM^(Mlm-jV9nUk07zHRh?_E9Tj`NIF%-Ll$DHfW!e zORWDuXqbsRv`}S1f$XvmP3D+ef230uZ1XRfh&&%b{x2XID-SIt16|w1o0njcfHj?P z)4S0p@WzP}EP0MH(kv8b(@fC@EE*CvlAg$kwo4K|y*cN@O*OL9(-l@`OO~q6BT@r( zv<&D^&Bj-ryR;9%NtA8b=af7wV`p=%;F8xylGJOPf?ANeAx&&xF5Fi@>4I7LGF$O2 fk|Cwvn_01iyqp+SICOP7-H)a)=hHbX2E*BGWwFKT literal 0 HcmV?d00001 diff --git a/MikroNUC/20260703/20260703004001-Config.rsc b/MikroNUC/20260703/20260703004001-Config.rsc new file mode 100644 index 0000000..c1a2a62 --- /dev/null +++ b/MikroNUC/20260703/20260703004001-Config.rsc @@ -0,0 +1,5317 @@ +# 2026-07-03 00:40:02 by RouterOS 7.23.1 +# system id = NmJcEM+8XAP +# +/caps-man channel +add band=2ghz-g/n extension-channel=disabled frequency=2422 name=2G-3 \ + reselect-interval=20m save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXX frequency=5660 name=5G-5660 \ + save-selected=yes +add band=5ghz-a/n/ac extension-channel=XXXXXXXX frequency=5160,5200,5280 \ + name=5G-5160-5280 reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5180 name=5G-36 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5200 name=5G-40 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5220 name=5G-44 \ + reselect-interval=30m save-selected=yes +add band=5ghz-n/ac extension-channel=XXXX frequency=5240 name=5G-48 \ + reselect-interval=30m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2442 name=2G-7 \ + reselect-interval=20m save-selected=yes +add band=2ghz-g/n extension-channel=disabled frequency=2462 name=2G-11 \ + reselect-interval=20m save-selected=yes +/interface bridge +add name=BridgeLAN +/interface ethernet +set [ find default-name=ether1 ] disable-running-check=no +/caps-man security +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=xs-Sec +add authentication-types=wpa2-psk encryption=aes-ccm group-encryption=aes-ccm \ + group-key-update=30m name=WPA2 +/disk +add slot=ram tmpfs-max-size=100000000 type=tmpfs +/interface list +add include=dynamic,static name=LAN +add exclude=LAN name=WAN +add exclude=LAN include=WAN name=Internet +/interface wireless security-profiles +set [ find default=yes ] supplicant-identity=MikroMIC +/iot wiliot servers +set *1 address=mqtt.us-east-2.prod.wiliot.cloud name="Wiliot US East" +/ip dhcp-server option +add code=12 name=HostName +add code=119 force=yes name=domain-search-list value="0x03'WORKGROUP'0x00" +/ip dhcp-server option sets +add name=P1505n options=HostName +add name=StartLife1 options=HostName +add name=amsterDev options=HostName +add name=MiTV options=HostName +add name=miller options=HostName +add name=amster options=HostName +add name=garage options=HostName +add name=domain-search-set options=domain-search-list +/ip pool +add name=dhcp ranges=10.30.30.150-10.30.30.200 +/routing table +add disabled=no fib name=to-awg +/system logging action +add email-start-tls=yes email-to=vados@vados.ru name=email target=email +/system script +add dont-require-permissions=no name=test owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + \r\ + \n#:local ScriptName [ :jobname ];\r\ + \n:local expSrc;\r\ + \n:local FileName;\r\ + \n:local FilePath;\r\ + \n:local DirPath \"tmpfs/\";\r\ + \n:global WaitForFile;\r\ + \n:global IfThenElse;\r\ + \n\r\ + \n:foreach Script in=([ /system/script find where name~\"Amster-GlobalConf\ + *\" ]) do={\r\ + \n:local ScriptVal [ /system/script/get \$Script ];\r\ + \n\r\ + \n:local expName [ (\$ScriptVal->\"name\") ];\r\ + \n#[/system/script get \$expScript name];\r\ + \n:delay 2s;\r\ + \n\r\ + \n:set expSrc [/system/script print where name=\$expName];\r\ + \n/system/script print where name=\$expName;\r\ + \n#[:put [/system/script get \$expScript source]];\r\ + \n:put [\$expSrc];\r\ + \n:delay 2s;\r\ + \n:set FileName \$expName;\r\ + \n:set FilePath (\$DirPath . \$FileName);\r\ + \n\r\ + \n#:log warning \"\\n----------------------\\n\\\$expSrc = \$expSrc\\n____\ + ______________________________\";\r\ + \n:log info \"\\n\\\$FileName = '\$FileName'\";\r\ + \n:log info \"\\n\\\$FilePath = \$FilePath\";\r\ + \n#:log warning {/system/script/get source=[/system/script/get \$expScript\ + \_source];};\r\ + \n\r\ + \n#\$[/system script get [/system script find name=\"\$FileName\"] source]\ + \r\ + \n\r\ + \n#:local gettingScript \"\$FileName\";\r\ + \n#:execute script=\"[[:parse \\\"[:parse { :put (\"\$[/system script get \ + [/system script find name=\$FileName] source]\"); } \\\"]]\"; \\\r\ + \n\r\ + \n#:execute script={:put \"[:parse \\\"[:parse [/system/script get [/syste\ + m/script find name=\$gettingScript] source];\\\"]]\";} \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n:do {\r\ + \n:local fName (\$FilePath . \".rsc\");\r\ + \n#:delay 3s;\r\ + \n#/file add name=\$fName;\r\ + \n:put [:tostr [\$expSrc]];\r\ + \n/file set \$fName contents=[:put [\$expSrc]];\r\ + \n:delay 3s;\r\ + \n\$WaitForFile \$fName;\r\ + \n(\$FilePath . \".rsc\");\r\ + \n#:delay 2s;\r\ + \n\r\ + \n} on-error={\r\ + \n:log error \"\\nERROR \\\$FilePath = \$FilePath\";\r\ + \n}\r\ + \n#\$WaitForFile (\$FilePath . \".rsc\");\r\ + \n#:execute script={ :put [/system/script get [:put \$FileName] source]; }\ + ;\r\ + \n#/system/script export \$Sscript file=\$FilePath;\r\ + \n#/system/script/export [:put [/system/script/get \$expScript source]] fi\ + le=\$FilePath;\r\ + \n#:execute script={ :put [/system/script/get \$expScript source]; } \\\r\ + \n\r\ + \n#:log warning \"\\n\\\$expScript = \$expScript\";\r\ + \n# \\\r\ + \n# file=(\$FilePath . \".rsc\\00\");\r\ + \n#script={:put [/system/script/get \$FileName source];} file=(\$FilePath\ + \_. \".rsc\\00\");\r\ + \n#/system script export \$src file=(\$FilePath . \$FileName);\r\ + \n}\r\ + \n\r\ + \n:log warning \"\\nComplete!\";\r\ + \n\r\ + \n # :execute script={ :put [ /system/script/get Amster-GlobalConfig sou\ + rce ]; } \\\r\ + \n # file=(\$FilePath . \".conf\\00\");\r\ + \n # \$WaitForFile (\$FilePath . \".conf\");\r\ + \n # :onerror Err {\r\ + \n # /tool/fetch upload=yes url=(\$BackupUploadUrl . \"/\" . \$FileName\ + \_. \".conf\") \\\r\ + \n # user=\$BackupUploadUser password=\$BackupUploadPass src-path=(\ + \$FilePath . \".conf\");\r\ + \n # :set ConfigFile [ /file/get (\$FilePath . \".conf\") ];\r\ + \n # :set (\$ConfigFile->\"name\") (\$FileName . \".conf\");\r\ + \n " +add dont-require-permissions=no name=test1 owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \r\ + \n#:local runningVer [:find [/system/resource/get version] \"(\"];\r\ + \n\r\ + \n:local runningChannel;\r\ + \n:local runningVersion;\r\ + \n:local ParseCustom;\r\ + \n\r\ + \n:set ParseCustom do={\r\ + \n :local Str [ :tostr \$1 ];\r\ + \n :local Start [:find [ \$Str ] [:tostr \$2]];\r\ + \n :local End [ :tostr \$3 ];\r\ + \n :local Num [ :tonum \$4 ];\r\ + \n :local Return;\r\ + \n\r\ + \n :if ([:len \$Num] = 0) do={:set Num \"0\"}\r\ + \n :local rest [:pick \$Str (\$Start+\$Num) [:len \$Str]]; \r\ + \n :if ([:len \$End ] = 0) do={:set Return [:pick \$Str \$Num \$Start]} e\ + lse={\r\ + \n :set End [:find \$rest \$End];\r\ + \n :set Return [:pick \$rest 0 \$End];\r\ + \n }\r\ + \n:return \$Return;\r\ + \n}\r\ + \n \r\ + \n:global runningChannel [\$ParseCustom [/system/resource/get version] \"(\ + \" \")\" 1];\r\ + \n:log warning [\$runningChannel];\r\ + \n:set runningVersion [\$ParseCustom [/system/resource/get version] \" \" \ + \")\" 2];\r\ + \n:log warning [\$runningVersion];\r\ + \n\r\ + \n\r\ + \n\r\ + \n# :if ([:len \$open] = 0) do={\r\ + \n# :log error \"\$SMP Could not extract installed OS channel from vers\ + ion string: `\$runningOsAndChannel`.\";\r\ + \n# :error \"\$SMP error, check logs\";\r\ + \n# }\r\ + \n# :local rest [:pick \$runningOsAndChannel (\$open+1) [:len \$runningOs\ + AndChannel]];\r\ + \n# :local close [:find \$rest \")\"];\r\ + \n# :local channel [:pick \$rest 0 \$close];\r\ + \n# :return \$channel;\r\ + \n#}" +add dont-require-permissions=no name=01-Amster-Init-Script owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# Amster Initial Script\r\ + \n#\r\ + \n#:global GlobalEnvRemove true; /system/script {run Amster-GlobalEnvRemov\ + e; };\r\ + \n#:delay 1s;\r\ + \n:#global ReloadGlobal true; \r\ + \n\r\ + \n:global AmScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global defOwner \"vados\";\r\ + \n:global AmFirstRun;\r\ + \n\r\ + \n:set AmFirstRun do={\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n #:global ParseKeyValueStore;\r\ + \n #:local AmUpdCRLF true;\r\ + \n #:local IfThenElse;\r\ + \n #:set IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1\ + \_] = true) do={:return \$2}; :return \$3}\r\ + \n #/certificate/settings/set builtin-trust-store=fetch;\r\ + \n #/tool/fetch \"\$scriptsUrl/certs/Root-YE.pem\" dst-path=\"root-ye.p\ + em\";\r\ + \n #/certificate/import file-name=\"root-ye.pem\" passphrase=\"\";\r\ + \n # For basic verification we rename the certificate and print it by f\ + ingerprint. Make sure exactly this one certificate (\"Root-YE\") is shown.\ + \r\ + \n #/certificate/set name=\"Root-YE\" [ find where common-name=\"Root Y\ + E\" ];\r\ + \n #/certificate/print proplist=name,fingerprint where fingerprint=\"e1\ + 4ffcad5b0025731006caa43a121a22d8e9700f4fb9cf852f02a708aa5d5666\";\r\ + \n #:log info \"\\nAmScriptsUrl = \$AmScriptsUrl\";\r\ + \n :local Scripts {\r\ + \n \"Amster-GlobalConfig\"\r\ + \n \"Amster-GlobalFunc\";\r\ + \n# \"Amster-SFTP-backup\"\r\ + \n# \"Amster-BkpEmail-and-upd\";\r\ + \n };\r\ + \n# /system/script/set owner=(\$ScriptVal->\"name\") \\\r\ + \n# source=[ \$IfThenElse (\$ScriptUpdatesCRLF = true) \$SourceCRLF \$S\ + ourceNew ] \$Script;\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n /system/script { run Amster-GlobalConfig; run Amster-GlobalFunc; }\r\ + \n}\r\ + \n:global AmUpdRun;\r\ + \n:set AmUpdRun do={\r\ + \n #:local UpdScript [ :tostr \$1 ];\r\ + \n #:global ParseKeyValueStore;\r\ + \n :global AmScriptsUrl;\r\ + \n :global defOwner;\r\ + \n :local Scripts {\r\ + \n# \"Amster-GlobalConfig\"\r\ + \n# \"Amster-GlobalFunc\"\r\ + \n# \"Amster-GlobalRemove\"\r\ + \n \"Amster-backup-SFTP\"\r\ + \n \"Amster-SFTP-backup\"\r\ + \n \"Amster-BkpEmail-and-upd\"\r\ + \n \"Amster-TG-Bot\"\r\ + \n \"Amster-TG-Notification\";\r\ + \n };\r\ + \n :foreach Script in=\$Scripts do={\r\ + \n :put \"updating \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$defOwner source=([ /too\ + l/fetch (\"\$AmScriptsUrl\" . \$Script . \".rsc\") output=user as-value ]-\ + >\"data\");\r\ + \n /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$\ + Script;\r\ + \n };\r\ + \n# /system/script/add name=\$Script owner=\$AmOwner source=([ /tool/fe\ + tch check-certificate=yes-without-crl (\"\$AmScriptsUrl\" . \$Script . \".\ + rsc\") output=user as-value ]->\"data\"); };\r\ + \n}\r\ + \n\r\ + \n\$AmFirstRun;\r\ + \n#\$AmUpdRun;\r\ + \n\r\ + \n#\$setUpdExist;\r\ + \n#:global setUpdExist;\r\ + \n#:global ParseKeyValueStore;\r\ + \n#:set setUpdExist do={\r\ + \n#:do {\r\ + \n#:global ParseKeyValueStore;\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\\r\?\\n#comment=*\" ] do={\r\ + \n# :local ScriptVal [ /system/script/get \$Script ];\r\ + \n# :local ScriptInfo [ \$ParseKeyValueStore (\$ScriptVal->\"#comment\"\ + ) ];\r\ + \n# :local scriptName (\$ScriptVal->\"name\");\r\ + \n#:log warning (\"\\n\\\$scriptName = \". \$scriptName);\r\ + \n#: log warning (\"\\n\\\$ScriptInfo = \". \$ScriptInfo);\r\ + \n# }\r\ + \n#} on-error={}\r\ + \n#}\r\ + \n#\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run\ + -count;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;\r\ + \n#;(evl / (evl /docommand=;(evl / (evl /foreachcounter=\$Script;do=;(evl \ + / (evl /localname=\$ScriptVal;value=(evl (evl /system/script/getnumber=\$S\ + cript))) (evl /localname=\$ScriptInfo;value=(evl (<%% \$ParseKeyValueStore\ + \_(> \$ParseKeyValueStore);(-> \$ScriptVal comment)))) (evl /localname=\$s\ + criptName) (evl /setname=\$scriptName;value=(-> #\$ScriptVal name)) (evl /\ + log/errormessage=(. \r\ + \n# \$ ScriptInfo = \$ScriptInfo)));in=(evl (evl /system/script/findwhere\ + =\$name;\$owner;\$policy;\$dont-require-permissions;\$last-started;\$run-c\ + ount;\$source;\$invalid;\$comment;\$.id;\$.nextid;\$.dead;\$.about;(~ \$so\ + urce (. ^#!rsc by Vados \r\ + \n# \? \r\ + \n#));5))));on-error=;(evl /)))\r\ + \n" +add dont-require-permissions=no name="Download cAP's Packages" owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# +------------------------------+\r\ + \n# | |\r\ + \n# | Mikrotik RouterOS Scripts |\r\ + \n# | _Download cAP's Packages_ |\r\ + \n# | |\r\ + \n# +------------------------------+\r\ + \n# https://github.com/sebastian13/mikrotik-routeros-scripts\r\ + \n#\r\ + \n\r\ + \n# Set a directory path providing the required space\r\ + \n# Add a microSD card https://wiki.mikrotik.com/wiki/Manual:System/Disks\ + \r\ + \n# or setup a RAM-Disk via '/disk/add type=tmpfs tmpfs-max-size=20M slot=\ + ram'\r\ + \n:local dir \"/tmpfs/capsman-packages\";\r\ + \n\r\ + \n\r\ + \n# Set Package Path if required, set policy to none if using 'upgrade-cap\ + s' script\r\ + \n/interface/wifi/capsman/set package-path=\$dir upgrade-policy=none;\r\ + \n/caps-man/manager/set package-path=\$dir upgrade-policy=none;\r\ + \n#require-same-version;\r\ + \n\r\ + \n:local installed [/system package get value-name=version [find where nam\ + e=\"routeros\"]]\r\ + \n\r\ + \n# Delete old downloads\r\ + \n# /file/remove [ find type=package name~\"capsman*\" ]\r\ + \n\r\ + \n# Starting from RouterOS version 7.13, drivers for older wireless and 60\ + GHz interfaces are\r\ + \n# part of the separate \"wireless\" package. The existing \"wifiwave2\" \ + package has been divided\r\ + \n# into distinct packages: \"wifi-qcom\" and \"wifi-qcom-ac\".\r\ + \n# RouterOS >=7.12 is required to upgrade to 7.13.\r\ + \n# https://forum.mikrotik.com/viewtopic.php\?t=202423\r\ + \n\r\ + \n:foreach architecture in={ \"arm\";\r\ + \n# \"arm64\";\r\ + \n \"x86_64\";\r\ + \n \"mipsbe\"} do={\r\ + \n :foreach package in={ \"routeros\";\r\ + \n# \"wifi-qcom-ac\";\r\ + \n# \"wifi-qcom\";\r\ + \n \"container\";\r\ + \n \"wireless\"} do={ :do {\r\ + \n :put \"\"\r\ + \n :put \"Download \$package \$installed for \$architecture\"\r\ + \n /tool fetch mode=https \\\r\ + \n url=\"https://download.mikrotik.com/routeros/\$installed/\$packa\ + ge-\$installed-\$architecture.npk\" \\\r\ + \n dst-path=\"\$dir/\$package-\$installed-\$architecture.npk\"\r\ + \n } on-error={ :put \"Error downloading the file. On a 404, note t\ + hat the package may not exist for the specified architecture!\" }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Script for setup routeros script system" \ + dont-require-permissions=no name=AM-setup owner=AM-setup policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-setup\r\ + \n# Script comment: Script for setup routeros script system\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# :local ScriptName [ :jobname ];\r\ + \n# Additional commands:\r\ + \n#\r\ + \n# For scripts:\r\ + \n#:global NewScript \"script_name\"; \r\ + \n#:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n#/system/script/add name=\$NewScript owner=\$NewScript source=([/tool/fe\ + tch check-certificate=yes-without-crl (\$GlobalScriptsUrl . \$ScriptAdd . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n#:global NewScript (a);\r\ + \n#:global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n#\r\ + \n# For run from terminal:\r\ + \n#/system/script/add name=AM-setup owner=AM-Setup source=([/tool/fetch ch\ + eck-certificate=yes-without-crl \"https://ros.vados.ru/AM-setup.rsc\" outp\ + ut=user as-value ]->\"data\");\r\ + \n#\r\ + \n\r\ + \n:global ScriptUpdatesCRLF \"LF\";\r\ + \n#:if (\$ScriptUpdatesCRLF != false) do={\r\ + \n# :foreach Script in=[ /system/script/find where source~\"^#!rsc by Vad\ + os\" ] do={\r\ + \n# if (\$ScriptUpdatesCRLF = true) do={\r\ + \n# /system/script/set source=[ :tocrlf [ get \$Script source ] ] \$S\ + cript;\r\ + \n# } else={\r\ + \n# /system/script/set source=[ :tolf [ get \$Script source ] ] \$Scr\ + ipt;\r\ + \n# }\r\ + \n# }\r\ + \n#}\r\ + \n\r\ + \n# /import verbose=no \$ScriptName\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global SetupScript [ :jobname ];\r\ + \n:global RemoveMe true;\r\ + \n:global RunOnce (a);\r\ + \n:global InitSetup true;\r\ + \n:global tmpfs false;\r\ + \n\r\ + \n{\r\ + \n :local RmMe [:tobool \$1];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :global SetupScript;\r\ + \n :global RemoveMe;\r\ + \n :global InitSetup;\r\ + \n :global tmpfs;\r\ + \n\r\ + \n :local UrlSuffix \$Identity;\r\ + \n :local UrlDir \"certs/\";\r\ + \n :local CertUrl (\$GlobalScriptsUrl . \$UrlDir . \$UrlSuffix .\"/\"); \ + \r\ + \n :local CertCommonName \"Root YE\";\r\ + \n :local CertFileName \"Root-YE.pem\";\r\ + \n :local CertFingerprint \"e14ffcad5b0025731006caa43a121a22d8e9700f4fb9c\ + f852f02a708aa5d5666\";\r\ + \n\r\ + \n :if (\$SetupScript = false) do={\r\ + \n :global SetupScript \"AM-setup\";\r\ + \n }\r\ + \n :if (\$RemoveMe != false) do={\r\ + \n :set RemoveMe true;\r\ + \n }\r\ + \n\r\ + \n :log warning (\"\\n\\\$GlobalScriptsUrl = \" . \$GlobalScriptsUrl);\r\ + \n \r\ + \n\r\ + \n :if (!(((\$CertSettings->\"builtin-trust-anchors\") = \"trusted\" ||\ + \_\\\r\ + \n (\$CertSettings->\"builtin-trust-store\") ~ \"fetch\" || \\\r\ + \n (\$CertSettings->\"builtin-trust-store\") = \"all\") && \\\r\ + \n [ :len [ /certificate/builtin/find where common-name=\$CertComm\ + onName ] ] > 0)) do={\r\ + \n :put \"Importing certificate...\";\r\ + \n /tool/fetch (\$CertUrl . \$CertFileName) dst-path=\$CertFileName\ + \_as-value;\r\ + \n :delay 1s;\r\ + \n /certificate/import file-name=\$CertFileName passphrase=\"\";\r\ + \n /certificate/set name=\"Root-YE\" [ find where common-name=\"Roo\ + t YE\" ];\r\ + \n :delay 1s;\r\ + \n };\r\ + \n :local CertSettings [ /certificate/settings/get ];\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] != 1) do={\r\ + \n :error \"Something is wrong with your certificates!\";\r\ + \n };\r\ + \n\r\ + \n:local MkTmpfs do={\r\ + \n :local TmpFs [ /disk/find where slot=tmpfs type=tmpfs ];\r\ + \n :if ([ :len \$TmpFs ] = 1) do={\r\ + \n :if ([ /disk/get \$TmpFs disabled ] = true) do={\r\ + \n :log info (\"The tmpfs is disabled, enabling.\");\r\ + \n /disk/enable \$TmpFs;\r\ + \n }\r\ + \n :return true;\r\ + \n }\r\ + \n :log info (\"Try to removing directory 'tmpfs'.\");\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/remove tmpfs;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n# \$RmDir \"tmpfs\";\r\ + \n :onerror Err {\r\ + \n /disk/add slot=tmpfs type=tmpfs tmpfs-max-size=([ /system/resource/g\ + et total-memory ] / 3);\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get tmpfs;\r\ + \n :return true;\r\ + \n } delay=222ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\"Creating disk of type tmpfs failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n}\r\ + \n:if (\$tmpfs = true) do={\r\ + \n :put \"Tmpfs variable is true. Checking for tmpfs/scripts directory...\ + \";\r\ + \n :if ([/file find name=\"tmpfs/scripts\"] != \"\") do={\r\ + \n :log info (\$0 . \"Directory tmpfs/scripts exsist.\");\r\ + \n :return \$true;\r\ + \n } else={\r\ + \n :log warning (\$0 . \"Directory tmpfs/scripts does't exsist.\");\r\ + \n :if ([ \$MkTmpfs ] = false) do={\r\ + \n :log error (\$0 . \"Directory tmpfs is not created!\");\r\ + \n :return false;\r\ + \n } else={\r\ + \n :onerror Err {\r\ + \n /file/add type=\"directory\" name=/tmpfs/scripts;\r\ + \n :do {\r\ + \n :retry {\r\ + \n /file/get /tmpfs/scripts;\r\ + \n :return true;\r\ + \n } delay=100ms max=10;\r\ + \n } on-error={ }\r\ + \n } do={\r\ + \n :log warning (\$0 . \"Making directory 'scripts' failed: \" . \$\ + Err);\r\ + \n :return false;\r\ + \n }\r\ + \n# :return true;\r\ + \n# [ /file/get \$FileName ]\r\ + \n }\r\ + \n# :log error (\$0 . \"Directory tmpfs/scripts does not exsist. Remove\ + \_global variable tmpfs!\");\r\ + \n# :global tmpfs (a);\r\ + \n# return false;\r\ + \n }\r\ + \n}\r\ + \n :put \"Renaming AM-GlobalConfig, if exists...\";\r\ + \n :local ConfOldName (\"AM-GlobalConfig-\" . [ /system/clock/get date ] \ + . [ /system/clock/get time ] . \".rsc\");\r\ + \n /system/script/set name=\$ConfOldName [ find where name=\"AM-GlobalCon\ + fig\" ];\r\ + \n :foreach Script in={ \"AM-GlobalConfig\"; \"AM-GlobalFunc\" } do={\r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetc\ + h check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \ + \".rsc\") output=user as-value ]->\"data\");\r\ + \n \r\ + \n /system/script run \$Script;\r\ + \n :delay 2s;\r\ + \n };\r\ + \n\r\ + \n :local Script \"mod/AM-Certs-By-Ident\";\r\ + \n \r\ + \n :put \"Installing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n /system/script/add name=\$Script owner=\$Script source=([ /tool/fetch \ + check-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".\ + rsc\") output=user as-value ]->\"data\");\r\ + \n /system/script run \$Script;\r\ + \n :global tmpAddCerts;\r\ + \n :while ([ \$tmpAddCerts ] = true) do={\r\ + \n :delay 1s;\r\ + \n }\r\ + \n :delay 1s;\r\ + \n :put \"Removing \$Script...\";\r\ + \n /system/script/remove [ find where name=\$Script ];\r\ + \n\r\ + \n :if ([ :len [ /certificate/find where fingerprint=\$CertFingerprint ] \ + ] > 0) do={\r\ + \n :put \"Renaming certificate by its common-name...\";\r\ + \n :global CertificateNameByCN;\r\ + \n \$CertificateNameByCN \$CertFingerprint;\r\ + \n };\r\ + \n\r\ + \n :if (\$RemoveMe = true) do={\r\ + \n :put \"Add Scheduler for remove this script...\";\r\ + \n :local OnEvent (\":delay 30s;\\n\" . \"/system/script/remove [ find \ + where name=\\\"\$SetupScript\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \"/system/scheduler/remove [ find where name=\\\ + \"_RemoveSetup\\\" ];\\n\" . \\\r\ + \n \":delay 2s;\\n\" . \":global RemoveMe (a);\\n\");\r\ + \n :local Comment \"_RemoveSetup tmp scheduler for remove setup script.\ + \";\r\ + \n /system/scheduler/add name=\"_RemoveSetup\" comment=\$Comment start-\ + time=startup interval=30s on-event=\$OnEvent;\r\ + \n :log warning (\$0 . \"\\nI've done everything, and now I'm tired..\ + . I'm leaving.\\nThere will be an error below, this is normal.\");\r\ + \n }\r\ + \n :delay 1;\r\ + \n :put \"Loading configuration and functions...\";\r\ + \n :do { \r\ + \n :global ScriptInstallUpdate; \$ScriptInstallUpdate;\r\ + \n :put \"Normal removing temporary global environment variables.\";\ + \r\ + \n :global SetupScript (a);\r\ + \n :global InitSetup (a);\r\ + \n :global RemoveMe (a);\r\ + \n } on-error={\r\ + \n :local ErrorMsg \"Loading configuration and functions error!\";\r\ + \n :log error (\$0 . \$ErrorMsg . :error);\r\ + \n :error \$ErrorMsg;\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Global Config Script" dont-require-permissions=no name=\ + AM-GlobalConfig owner=AM-GlobalConfig policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GlobalConfig\r\ + \n# Script comment: Global Config Script\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# Warning: Dont touch this line!\r\ + \n:global Identity [/system/identity/get name];\r\ + \n:global GlobalConfReady false;\r\ + \n#\r\ + \n#\r\ + \n# Global Url For Amster scripts to fetch\r\ + \n# most be defined in AM-Init-Script or AM-setup scripts\r\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\r\ + \n:global ScriptsUrlSuffix (a);\r\ + \n:global CheckSumsVerify true;\r\ + \n\r\ + \n:global ScriptUpdatesCRLF true;\r\ + \n:global CommentsInScripts true;\r\ + \n\r\ + \n# Debug output:\r\ + \n:global PrintDebug true; #false;\r\ + \n\r\ + \n# Debug output for specific script:\r\ + \n#:set (\$PrintDebugOverride->\"AM-GlobalFunc\") true;\r\ + \n\r\ + \n# Debug logs\r\ + \n# Other actions (disk, email, remote or support) can be used as well.\r\ + \n# I do not recommend using echo - use debug output instead (https://rsc.\ + eworm.de/main/DEBUG.html#debug-output).\r\ + \n#/system/logging/add topics=script,debug action=memory;\r\ + \n\r\ + \n:global MikrotikUpgradeUrl \"https://upgrade.mikrotik.com/routeros\";\r\ + \n:global Domain \"WORKGROUP\";\r\ + \n\r\ + \n:global EmailGeneralTo \"vados@vados.ru\";\r\ + \n#:global EmailGeneralCc \"\";\r\ + \n\r\ + \n# \$ScriptInstallUpdate mod/notification-telegram\r\ + \n:global TelegramTokenId \"8390868324:AAG3_d1hfVR2M6pGAgDK6ZvhOe4J97nyopw\ + \";\r\ + \n:global TelegramChatId \"-1003631309063\"; #\"454403445\";\r\ + \n# Use this to send notifications to a specific topic in group.\r\ + \n#:global TelegramThreadId \"30\";\r\ + \n# Using telegram-chat you have to define trusted chat ids (not group ids\ + !)\r\ + \n# or user names. Groups allow to chat with devices simultaneously.\r\ + \n:global TelegramChatIdsTrusted {\r\ + \n \"454403445\"\r\ + \n \"-1003631309063\"\r\ + \n \"-5124482807\"\r\ + \n \"1087968824\";\r\ + \n};\r\ + \n# \"-5294757340\";\r\ + \n\r\ + \n:global TelegramChatGroups \"(all)\";\r\ + \n#:global TelegramChatGroups \"(all|home|office)\";\r\ + \n\r\ + \n:global NotificationFunctions {\r\ + \n \"email\"\r\ + \n \"telegram\";\r\ + \n};\r\ + \n# Toggle this to disable symbols in notifications.\r\ + \n:global NotificationsWithSymbols true;\r\ + \n:global TerminalColorOutput true;\r\ + \n\r\ + \n:global DetectIpAddrDefault \"https://ipv4.mikrotik.ovh/\";\r\ + \n:global DetectIpAddrFallback \"https://api.ipify.org/\";\r\ + \n\r\ + \n## Update channel. Possible values: stable, long-term, testing, developm\ + ent\r\ + \n:global updateChannel \"stable\";\r\ + \n\r\ + \n:global CertRenewUrl (\$GlobalScriptsUrl . \"certs/\" . \$Identity . \"/\ + \");\r\ + \n:global CertRenewTime 2w;\r\ + \n:global CertRenewPass (a); #{\r\ + \n# \"\";\r\ + \n# \"v3ry-s3cr3t\";\r\ + \n# \"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n:global CertWarnTime 1w;\r\ + \n:global CertIssuedExportPass (a); # {\r\ + \n# \"\";\r\ + \n# \"cert1-cn\"=\"v3ry-s3cr3t\";\r\ + \n# \"cert2-cn\"=\"4n0th3r-s3cr3t\";\r\ + \n#};\r\ + \n\r\ + \n# Script BackupAndUpdate mode, possible values: backup,\r\ + \n# updateOnce - Update once as \"osnotify\" and back \"osnotify\" mode (i\ + n terminal run :set OSUpdateMode updateOnce; \$OSUpdateMode;). \r\ + \n# osupdate - Update if available and creates backups before/after update\ + \_(ignores `forceBackup`)\r\ + \n# Set `forceBackup` to true to always create backups, even\ + \_without updates\r\ + \n# osnotify - Set `forceBackup` to always create backups on every run\r\ + \n:global DevInfoIncludeIP true;\r\ + \n:global BackupAndUpdateMode \"osnotify\"\r\ + \n\r\ + \n:global forceBackup true;\r\ + \n:global BackupPassword \"Peskar55\";\r\ + \n\r\ + \n# SFTP Backup Variables\r\ + \n:global BackupRandomDelay 0;\r\ + \n:global BackupUploadUrl \"sftp://amster.vados.ru/upload/\";\r\ + \n:global BackupUploadUser \$Identity;\r\ + \n:global BackupUploadPass \"root.mnuc.backuper\";\r\ + \n\r\ + \n:global BackupSendBinary true;\r\ + \n:global BackupSendExport true;\r\ + \n:global BackupSendGlobalConfig true;\r\ + \n:global BackupSendScripts true;\r\ + \n\r\ + \n# Remove local file after uploading if no errors\r\ + \n:global BackupRmLocal true;\r\ + \n\r\ + \n# Local Backups Root Directory \r\ + \n:global BackupLocalRoot \"tmpfs\";\r\ + \n\r\ + \n# Encrypt Backup\r\ + \n:global BackupEncrypt true;\r\ + \n\r\ + \n# Sensitive information in Backups\r\ + \n:global BackupSens true;\r\ + \n\r\ + \n# User Export\r\ + \n:global BackupUser true;\r\ + \n\r\ + \n# License Export (not for CHR, will silently skip)\r\ + \n:global BackupLicense false;\r\ + \n\r\ + \n# SSH Keys\r\ + \n:global BackupSshKeys true;\r\ + \n\r\ + \n# Certificate Export\r\ + \n:global BackupCerts true;\r\ + \n\r\ + \n# Certificate Password\r\ + \n:global BackupCertsPasswd \"\";\r\ + \n\r\ + \n# User-Manager Export\r\ + \n:global BackupUserMan false;\r\ + \n\r\ + \n# The Dude Export\r\ + \n:global BackupDude false;\r\ + \n\r\ + \n# User Files to export, comma-separated string or array of strings\r\ + \n# User Files are not removed on backup\r\ + \n# Any directory paths will be removed (/ -> _) on remote file\r\ + \n# Nonpresent files are silently skipped\r\ + \n:global BackupUserList \"autosupout.rif,autosupout.old.rif\";\r\ + \n\r\ + \n# load functions and any custom scripts\r\ + \n# [ /system/script/find where name=\"global-config-ov\ + erlay\" ], \\\r\ + \n# [ /system/script/find where name~\"^global-config-o\ + verlay\\\\.d/.\" ]\r\ + \n#\r\ + \n#:foreach Script in=([ /system/script/find where name=\"Amster-TGglobalV\ + ars\" ], [ /system/script/find where name=\"Amster-TGglobalFunc\" ]) do={\ + \r\ + \n# :onerror Err {\r\ + \n# /system/script/run \$Script;\r\ + \n# } do={\r\ + \n# :log error (\"Loading configuration from configs and functions\"\ + \_. [ /system/script/get \$Script name ] . \" failed: \" . \$Err);\r\ + \n# }\r\ + \n#}\r\ + \n#:log info \"\\ndevMode = \$devMode\";\r\ + \n#:log info \"\\ndevRunning = \$devRunning\";\r\ + \n:foreach Script in=[ /system/script/find where source~\"^#!rsc by Vados \ + GlobalRUN\\r\?\\n\" ] do={\r\ + \n :onerror Err {\r\ + \n /system/script/run \$Script;\r\ + \n } do={\r\ + \n :log error (\"Run script \" . [ /system/script/get \$Script name \ + ] . \" width marker \\\" GlobalRUN\\\" failed: \" . \$Err);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n# signal we are ready\r\ + \n:set GlobalConfReady true;\r\ + \n:while (\$GlobalConfReady != true) do={:delay 500ms};\r\ + \n" +add comment="Global functions" dont-require-permissions=no name=AM-GlobalFunc \ + owner=AM-GlobalFunc policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-GglobalFunc\r\ + \n# Script comment: Global functions\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n#:global checkRunJob \$SriptName;\r\ + \n#:global fixCRLF;\r\ + \n#:if (\$fixCRLF != false) do={\r\ + \n#:foreach fixScript in=([ /system/script/find where name~\"*\" ]) do={/s\ + ystem/script/set source=[ :tocrlf [ get \$fixScript source ] ] \$fixScript\ + }\r\ + \n#:if (\$fixCRLF != \"LF\") do={ /system/script/set source=[ :tocrlf [ g\ + et \$fixScript source ] ] \$fixScript};};\r\ + \n#}\r\ + \n:global GlobalFuncReady false;\r\ + \n\r\ + \n# Git commit id & info, expected configuration version\r\ + \n:global CommitId \"main/729/f42d43ae\";\r\ + \n:global CommitTAG \"unknown\";\r\ + \n:global CommitInfo \"Autocommit in repo: ros.vados.ru, generated by owne\ + r: root, in 00:05:03 01.07.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="#\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.10.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.10.pub\";\ + \n#\ + \n#root.mnuc.backuper\ + \n\ + \n#/tool/fetch mode=https url=(\$GlobalScriptsUrl . \".bak/mikro_bak_rsa@1\ + 0.30.30.81.pub\") dst-path=\"/mikro_bak_rsa@10.30.30.81.pub\";\ + \n#:local Script AM-GlobalConfig;\ + \n#/system/script/add name=\$Script owner=\$Script source=([ /tool/fetch c\ + heck-certificate=yes-without-crl url=(\$GlobalScriptsUrl . \$Script . \".r\ + sc\") output=user as-value ]->\"data\");\ + \n#/usb1/mikro_bak_rsa.pub\ + \n#:local FetchFile AM-DOHCert.rsc\ + \n\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/tmp/\";\ + \n:global MkDir;\ + \n\ + \n:local FetchFile Manager@id_rsa.pub\ + \n:local localDir \"/\";\ + \n\ + \n\$MkDir \$localDir;\ + \n\ + \n/tool/fetch mode=https url=(\$GlobalScriptsUrl . \$FetchFile) dst-path=(\ + \$localDir . \$FetchFile);\ + \n" +add dont-require-permissions=yes name=MkTmpfs owner=vados policy=\ + ftp,read,write,policy,test,sniff,sensitive source=\ + ":global MkDir;\r\ + \n\r\ + \n\$MkDir /usb1/tmpfs;" +add dont-require-permissions=no name=CreateContainer owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + local contIF \"veth1\";\r\ + \n:local contIP \"10.10.10.10/24\";\r\ + \n:local contGW \"10.10.10.1\";\r\ + \n#/interface/veth/add name=\$contIF address=\$contIP gateway=\$contGW;\r\ + \n:local contBridgeIF \"Bridge-DOC\";\r\ + \n:local contBridgeIP \"10.10.10.1/24\";\r\ + \n/interface/bridge/add name=\$contBridgeIF;\r\ + \n/ip/address/add address=\$contBridgeIP interface=\$contBridgeIF;\r\ + \n/interface/bridge/port/add bridge=\$contBridgeIF interface=\$contIF;\r\ + \n/container/config/set ram-high=768M registry-url=https://registry-1.dock\ + er.io tmpdir=sata1/tmp\r\ + \n" +add dont-require-permissions=no name=carkeeperbot owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/mounts/add list=carkeeper_configs/.profile src=sata1/carkeeper/c\ + onfigs/.profile dst=/root/.ashrc;\r\ + \n/container/mounts/add list=carkeeper_configs/php.ini src=sata1/carkeeper\ + /configs/php.ini dst=/etc/php82/php.ini;\r\ + \n#/container/mounts/add list=carkeeper_configs src=sata1/carkeeper/config\ + s dst=/configs;\r\ + \n/container/mounts/add list=carkeeper_app src=sata1/carkeeper/app dst=/ap\ + p;\r\ + \n/container/mounts/add list=carkeeper_scripts src=sata1/carkeeper/scripts\ + \_dst=/scripts;\r\ + \n/container/mounts/add list=carkeeper_configs/sshd_config src=sata1/carke\ + eper/configs/sshd_config dst=/etc/ssh/sshd_config;\r\ + \n\r\ + \n/container/mounts/add list=carkeeper_tmp src=sata1/carkeeper/tmp dst=/va\ + r/tmp;\r\ + \n/container/mounts/add list=carkeeper_sync src=sata1/carkeeper/sync dst=/\ + var/sync;\r\ + \n" +add comment="Enable or disable ip addresses based on bridge port state" \ + dont-require-permissions=no name=mod/AM-IPbridge owner=mod/AM-IPbridge \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-IPbridge\r\ + \n# Script comment: Enable or disable ip addresses based on bridge port st\ + ate\r\ + \n# Copyright (c) 2005-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n# Add this Script scheduler\r\ + \n:if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) do\ + ={\r\ + \n:log warning (\"SystemScheduler for script: \" . \$ScriptName . \" is NO\ + T SET! Fix It Now!\");\r\ + \n:local OnEvent \"/system/script/run \$ScriptName;\";\r\ + \n:local Comment \"Every Day Check For Fake Bridges\";\r\ + \n/system/scheduler/add name=\$ScriptName comment=\$Comment start-time=sta\ + rtup interval=1d on-event=\$OnEvent;\r\ + \n}\r\ + \n\r\ + \n:foreach Bridge in=[ /interface/bridge/find ] do={\r\ + \n :local BrName [ /interface/bridge/get \$Bridge name ];\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName ] ] > \ + 0) do={\r\ + \n :if ([ :len [ /interface/bridge/port/find where bridge=\$BrName and \ + inactive=no ] ] = 0) do={\r\ + \n /ip/address/disable [ find where !dynamic interface=\$BrName ];\r\ + \n } else={\r\ + \n /ip/address/enable [ find where !dynamic interface=\$BrName ];\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n" +add comment="Use Telegram to chat with your Router and send commands" \ + dont-require-permissions=no name=mod/AM-TG-Bot owner=mod/AM-TG-Bot \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Bot\r\ + \n# Script comment: Use Telegram to chat with your Router and send command\ + s\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global Identity;\r\ + \n :global TelegramChatActive;\r\ + \n :global TelegramChatGroups;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdsTrusted;\r\ + \n :global TelegramChatOffset;\r\ + \n :global TelegramChatRunTime;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramRandomDelay;\r\ + \n :global TelegramTokenId;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global EscapeForRegEx;\r\ + \n :global FileExists;\r\ + \n :global GetRandom20CharAlNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global LogPrintVerbose;\r\ + \n :global MAX;\r\ + \n :global MIN;\r\ + \n :global MkDir;\r\ + \n :global RandomDelay;\r\ + \n :global RmDir;\r\ + \n :global ScriptLock;\r\ + \n :global SendTelegram2;\r\ + \n :global SymbolForNotification;\r\ + \n :global ValidateSyntax;\r\ + \n :global WaitForFile;\r\ + \n :global WaitFullyConnected;\r\ + \n\r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$WaitFullyConnected;\r\ + \n\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$ScriptName ] ] = 0) \ + do={\r\ + \n /system/scheduler/add start-time=startup interval=30s name=\$ScriptN\ + ame on-event=\"/system/script/run \$ScriptName;\" comment=\"Telegram Chat \ + Bot\";\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramChatOffset ] != \"array\") do={\r\ + \n :set TelegramChatOffset { 0; 0; 0 };\r\ + \n }\r\ + \n :if ([ :typeof \$TelegramRandomDelay ] != \"num\") do={\r\ + \n :set TelegramRandomDelay 0;\r\ + \n }\r\ + \n\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Downloading required certificate \ + failed.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n \$RandomDelay \$TelegramRandomDelay;\r\ + \n\r\ + \n :local Data false;\r\ + \n :for I from=1 to=4 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl outpu\ + t=user \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/get\ + Updates\?offset=\" . \\\r\ + \n \$TelegramChatOffset->0 . \"&allowed_updates=%5B%22message%22%\ + 5D\") as-value ]->\"data\");\r\ + \n :set TelegramRandomDelay [ \$MAX 0 (\$TelegramRandomDelay - 1) ]\ + ;\r\ + \n } do={\r\ + \n :if (\$I < 4) do={\r\ + \n \$LogPrint debug \$ScriptName (\"Fetch failed, \" . \$I . \". \ + try: \" . \$Err);\r\ + \n :set TelegramRandomDelay [ \$MIN 15 (\$TelegramRandomDelay + 5\ + ) ];\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n \$LogPrint warning \$ScriptName (\"Failed getting updates.\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local UpdateID 0;\r\ + \n :local Uptime [ /system/resource/get uptime ];\r\ + \n :foreach Update in=(\$JSON->\"result\") do={\r\ + \n :set UpdateID (\$Update->\"update_id\");\r\ + \n \$LogPrintVerbose debug \$ScriptName (\"Update \" . \$UpdateID . \":\ + \_\" . [ :serialize to=json \$Update ]);\r\ + \n\r\ + \n :local Message (\$Update->\"message\");\r\ + \n :local IsAnyReply ([ :typeof (\$Message->\"reply_to_message\") ] = \ + \"array\");\r\ + \n :local IsMyReply (\$TelegramMessageIDs->[ :tostr (\$Message->\"reply\ + _to_message\"->\"message_id\") ]);\r\ + \n :if ((\$IsMyReply = 1 || \$TelegramChatOffset->0 > 0 || \$Uptime > 5\ + m) && \$UpdateID >= \$TelegramChatOffset->2) do={\r\ + \n :local Trusted false;\r\ + \n :local Chat (\$Message->\"chat\");\r\ + \n :local From (\$Message->\"from\");\r\ + \n :local Command (\$Message->\"text\");\r\ + \n :local ThreadId [ \$IfThenElse (\$Message->\"is_topic_message\") (\ + \$Message->\"message_thread_id\") \"\" ];\r\ + \n\r\ + \n :foreach IdsTrusted in=(\$TelegramChatId, \$TelegramChatIdsTrusted\ + ) do={\r\ + \n :if (\$From->\"id\" = \$IdsTrusted || \\\r\ + \n \$From->\"username\" = \$IdsTrusted || \\\r\ + \n \$Chat->\"id\" = \$IdsTrusted) do={\r\ + \n :set Trusted true;\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Trusted = true) do={\r\ + \n :local Done false;\r\ + \n :if (\$Command = \"\?\") do={\r\ + \n \$LogPrint info \$ScriptName (\"Sending notice for update \" .\ + \_\$UpdateID . \".\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=([ \$IfThenElse ([ :len (\$From->\"first_name\") ] >\ + \_0) (\"Hello \" . (\$From->\"first_name\") . \"!\\n\\n\") ] . \\\r\ + \n \"Online\" . [ \$IfThenElse \$TelegramChatActive \" (and a\ + ctive!)\" ] . \", awaiting your commands!\") });\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && [ :pick \$Command 0 1 ] = \"!\") do={\r\ + \n :if (\$Command ~ (\"^! *(\" . [ \$EscapeForRegEx \$Identity ] \ + . \"|@\" . \$TelegramChatGroups . \")\\\$\")) do={\r\ + \n :set TelegramChatActive true;\r\ + \n } else={\r\ + \n :set TelegramChatActive false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Now \" . [ \$IfThenElse \$Tele\ + gramChatActive \"active\" \"passive\" ] . \\\r\ + \n \" from update \" . \$UpdateID . \"!\");\r\ + \n :set Done true;\r\ + \n }\r\ + \n :if (\$Done = false && (\$IsMyReply = 1 || (\$IsAnyReply = false\ + \_&& \\\r\ + \n \$TelegramChatActive = true)) && [ :len \$Command ] > 0) do\ + ={\r\ + \n :if ([ \$ValidateSyntax \$Command ] = true) do={\r\ + \n :local State \"\";\r\ + \n :local File (\"tmpfs/telegram-chat/\" . [ \$GetRandom20CharA\ + lNum 6 ]);\r\ + \n :if ([ \$MkDir \"tmpfs/telegram-chat\" ] = false) do={\r\ + \n \$LogPrint error \$ScriptName (\"Failed creating directory\ + !\");\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n \$LogPrint info \$ScriptName (\"Running command from update \ + \" . \$UpdateID . \": \" . \$Command);\r\ + \n :execute script=(\":do {\\n\" . \$Command . \"\\n} on-error=\ + { /file/add name=\\\"\" . \$File . \".failed\\\" };\" . \\\r\ + \n \"/file/add name=\\\"\" . \$File . \".done\\\"\") file=(\$\ + File . \"\\00\");\r\ + \n :if ([ \$WaitForFile (\$File . \".done\") [ \$EitherOr \$Tel\ + egramChatRunTime 20s ] ] = false) do={\r\ + \n :set State ([ \$SymbolForNotification \"warning-sign\" ] .\ + \_\"The command did not finish, still running in background.\\n\\n\");\r\ + \n }\r\ + \n :if ([ \$FileExists (\$File . \".failed\") ] = true) do={\r\ + \n :set State ([ \$SymbolForNotification \"cross-mark\" ] . \ + \"The command failed with an error!\\n\\n\");\r\ + \n }\r\ + \n :local Content ([ /file/read chunk-size=32768 file=\$File as\ + -value ]->\"data\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=true; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n \$State . [ \$IfThenElse ([ :len \$Content ] > 0) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"Output:\\n\" .\ + \_\$Content) \\\r\ + \n ([ \$SymbolForNotification \"memo\" ] . \"No output.\") \ + ]) });\r\ + \n \$RmDir \"tmpfs/telegram-chat\";\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName (\"The command from update \" .\ + \_\$UpdateID . \" failed syntax validation!\");\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\ + \"); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \ + \\\r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \ + \"Telegram Chat\"); \\\r\ + \n message=([ \$SymbolForNotification \"gear\" ] . \"Command:\ + \\n\" . \$Command . \"\\n\\n\" . \\\r\ + \n [ \$SymbolForNotification \"cross-mark\" ] . \"The comma\ + nd failed syntax validation!\") });\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n :local MessageText (\"Received a message from untrusted contact \ + \" . \\\r\ + \n [ \$IfThenElse ([ :len (\$From->\"username\") ] = 0) \"without\ + \_username\" (\"'\" . \$From->\"username\" . \"'\") ] . \\\r\ + \n \" (ID \" . \$From->\"id\" . \") in update \" . \$UpdateID . \ + \"!\");\r\ + \n :if (\$Command ~ (\"^! *\" . [ \$EscapeForRegEx \$Identity ] . \ + \"\\\$\")) do={\r\ + \n \$LogPrint warning \$ScriptName \$MessageText;\r\ + \n \$SendTelegram2 ({ origin=\$ScriptName; chatid=(\$Chat->\"id\"\ + ); silent=false; \\\r\ + \n replyto=(\$Message->\"message_id\"); threadid=\$ThreadId; \\\ + \r\ + \n subject=([ \$SymbolForNotification \"speech-balloon\" ] . \"\ + Telegram Chat\"); \\\r\ + \n message=(\"You are not trusted.\") });\r\ + \n } else={\r\ + \n \$LogPrint info \$ScriptName \$MessageText;\r\ + \n }\r\ + \n }\r\ + \n } else={\r\ + \n \$LogPrint debug \$ScriptName (\"Already handled update \" . \$Upd\ + ateID . \".\");\r\ + \n }\r\ + \n }\r\ + \n :set TelegramChatOffset ([ :pick \$TelegramChatOffset 1 3 ], \\\r\ + \n [ \$IfThenElse (\$UpdateID >= \$TelegramChatOffset->2) (\$UpdateID +\ + \_1) (\$TelegramChatOffset->2) ]);\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add comment="Script send Notifications in Telegram" dont-require-permissions=\ + no name=mod/AM-TG-Notifications owner=mod/AM-TG-Notifications policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: mod/AM-TG-Notifications\r\ + \n# Script comment: Script send Notifications in Telegram\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# requires device-mode, fetch, scheduler\r\ + \n:global FlushTelegramQueue;\r\ + \n:global GetTelegramChatId;\r\ + \n:global NotificationFunctions;\r\ + \n:global PurgeTelegramQueue;\r\ + \n:global SendTelegram;\r\ + \n:global SendTelegram2;\r\ + \n# flush telegram queue\r\ + \n:set FlushTelegramQueue do={ :onerror Err {\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global CertificateAvailable;\r\ + \n :global IsFullyConnected;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$IsFullyConnected ] = false) do={\r\ + \n \$LogPrint debug \$0 (\"System is not fully connected, not flushing.\ + \");\r\ + \n :return false;\r\ + \n }\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local AllDone true;\r\ + \n :local QueueLen [ :len \$TelegramQueue ];\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueue\ + \" ] ] > 0 && \$QueueLen = 0) do={\r\ + \n \$LogPrint warning \$0 (\"Flushing Telegram messages from scheduler,\ + \_but queue is empty.\");\r\ + \n }\r\ + \n :foreach Id,Message in=\$TelegramQueue do={\r\ + \n :if ([ :typeof \$Message ] = \"array\" ) do={\r\ + \n :onerror Err {\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl out\ + put=user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . (\$Message->\"tokenid\") .\ + \_\"/sendMessage\") \\\r\ + \n http-data=(\$Message->\"http-data\") as-value ]->\"data\");\r\ + \n :set (\$TelegramQueue->\$Id);\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json v\ + alue=\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint debug \$0 (\"Sending queued Telegram message failed: \ + \" . \$Err);\r\ + \n :set AllDone false;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$AllDone = true && \$QueueLen = [ :len \$TelegramQueue ]) do={\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ]\ + ;\r\ + \n :set TelegramQueue;\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# get the chat id\r\ + \n:set GetTelegramChatId do={ :onerror Err {\r\ + \n :global TelegramTokenId;\r\ + \n :global CertificateAvailable;\r\ + \n :global LogPrint;\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority - \ + G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\"\ + );\r\ + \n :return false;\r\ + \n }\r\ + \n :local Data;\r\ + \n :onerror Err {\r\ + \n :set Data ([ /tool/fetch check-certificate=yes-without-crl output=us\ + er \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TelegramTokenId . \"/getUpd\ + ates\?offset=0\" . \\\r\ + \n \"&allowed_updates=%5B%22message%22%5D\") as-value ]->\"data\");\ + \r\ + \n } do={\r\ + \n \$LogPrint warning \$0 (\"Fetching data failed: \" . \$Err);\r\ + \n :return false;\r\ + \n }\r\ + \n :local JSON [ :deserialize from=json value=\$Data ];\r\ + \n :local Count [ :len (\$JSON->\"result\") ];\r\ + \n :if (\$Count = 0) do={\r\ + \n \$LogPrint info \$0 (\"No message received.\");\r\ + \n :return false;\r\ + \n }\r\ + \n :local Message (\$JSON->\"result\"->(\$Count - 1)->\"message\");\r\ + \n \$LogPrint info \$0 (\"The chat id is: \" . (\$Message->\"chat\"->\"id\ + \"));\r\ + \n :if ((\$Message->\"is_topic_message\") = true) do={\r\ + \n \$LogPrint info \$0 (\"The thread id is: \" . (\$Message->\"message_\ + thread_id\"));\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set (\$NotificationFunctions->\"telegram\") do={\r\ + \n :local Notification \$1;\r\ + \n :global Identity;\r\ + \n :global IdentityExtra;\r\ + \n :global TelegramChatId;\r\ + \n :global TelegramChatIdOverride;\r\ + \n :global TelegramMessageIDs;\r\ + \n :global TelegramQueue;\r\ + \n :global TelegramThreadId;\r\ + \n :global TelegramThreadIdOverride;\r\ + \n :global TelegramTokenId;\r\ + \n :global TelegramTokenIdOverride;\r\ + \n :global CertificateAvailable;\r\ + \n :global CharacterReplace;\r\ + \n :global EitherOr;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global ProtocolStrip;\r\ + \n :global SymbolForNotification;\r\ + \n :global UrlEncode;\r\ + \n :local EscapeMD do={\r\ + \n :local Text [ :tostr \$1 ];\r\ + \n :local Mode [ :tostr \$2 ];\r\ + \n :local Excl [ :tostr \$3 ];\r\ + \n :global CharacterReplace;\r\ + \n :global IfThenElse;\r\ + \n :local Chars {\r\ + \n \"body\"={ \"\\\\\"; \"`\" };\r\ + \n \"plain\"={ \"_\"; \"*\"; \"[\"; \"]\"; \"(\"; \")\"; \"~\"; \"`\"\ + ; \">\";\r\ + \n \"#\"; \"+\"; \"-\"; \"=\"; \"|\"; \"{\"; \"}\"; \".\"; \ + \"!\" };\r\ + \n }\r\ + \n :foreach Char in=(\$Chars->\$Mode) do={\r\ + \n :if ([ :typeof [ :find \$Excl \$Char ] ] = \"nil\") do={\r\ + \n :set Text [ \$CharacterReplace \$Text \$Char (\"\\\\\" . \$Char)\ + \_];\r\ + \n }\r\ + \n }\r\ + \n :if (\$Mode = \"body\") do={:return (\"```\\n\" . \$Text . \"\\n```\ + \")}\r\ + \n :return \$Text;\r\ + \n }\r\ + \n :local ChatId [ \$EitherOr (\$Notification->\"chatid\") \\\r\ + \n [ \$EitherOr (\$TelegramChatIdOverride->(\$Notification->\"origin\")\ + ) \$TelegramChatId ] ];\r\ + \n :local ThreadId [ \$EitherOr (\$Notification->\"threadid\") \\\r\ + \n [ \$EitherOr (\$TelegramThreadIdOverride->(\$Notification->\"origin\ + \")) \\\r\ + \n [ \$IfThenElse ([ :len (\$TelegramChatIdOverride->(\$Notification->\ + \"origin\")) ] = 0) \$TelegramThreadId ] ] ];\r\ + \n :local TokenId [ \$EitherOr (\$TelegramTokenIdOverride->(\$Notificatio\ + n->\"origin\")) \$TelegramTokenId ];\r\ + \n :if ([ :len \$TokenId ] = 0 || [ :len \$ChatId ] = 0) do={:return fals\ + e}\r\ + \n :if ([ :typeof \$TelegramMessageIDs ] = \"nothing\") do={:set Telegram\ + MessageIDs ({})}\r\ + \n :local Truncated false;\r\ + \n :local Text (\"*__\" . [ \$EscapeMD (\"[\" . \$IdentityExtra . \$Ident\ + ity . \"] \" . \\\r\ + \n (\$Notification->\"subject\")) \"plain\" ] . \"__*\\n\\n\");\r\ + \n :local LenSubject [ :len \$Text ];\r\ + \n :local LenMessage [ :len (\$Notification->\"message\") ];\r\ + \n :local LenLink ([ :len (\$Notification->\"link\") ] * 2);\r\ + \n :local LenSum (\$LenSubject + \$LenMessage + \$LenLink);\r\ + \n :if (\$LenSum > 3968) do={\r\ + \n :set Text (\$Text . [ \$EscapeMD ([ :pick (\$Notification->\"message\ + \") 0 (3840 - \$LenSubject - \$LenLink) ] . \"...\") \"body\" ]);\r\ + \n :set Truncated true;\r\ + \n } else={:set Text (\$Text . [ \$EscapeMD (\$Notification->\"message\")\ + \_\"body\" ])}\r\ + \n :if (\$LenLink > 0) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"link\" ] .\ + \_\\\r\ + \n \"[\" . [ \$EscapeMD [ \$ProtocolStrip (\$Notification->\"link\") \ + ] \"plain\" ] . \"]\" . \\\r\ + \n \"(\" . [ \$EscapeMD (\$Notification->\"link\") \"plain\" ] . \")\ + \");\r\ + \n }\r\ + \n :if (\$Truncated = true) do={\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"scissors\"\ + \_] . \\\r\ + \n [ \$EscapeMD (\"The message was too long and has been truncated, c\ + ut off _\" . \\\r\ + \n ((\$LenSum - [ :len \$Text ]) * 100 / \$LenSum) . \"%_!\") \"plain\ + \" \"_\" ]);\r\ + \n }\r\ + \n :local HTTPData (\"chat_id=\" . \$ChatId . \"&disable_notification=\" \ + . (\$Notification->\"silent\") . \\\r\ + \n \"&reply_to_message_id=\" . (\$Notification->\"replyto\") . \"&mes\ + sage_thread_id=\" . \$ThreadId . \\\r\ + \n \"&disable_web_page_preview=true&parse_mode=MarkdownV2\");\r\ + \n :onerror Err {\r\ + \n :if ([ \$CertificateAvailable \"Go Daddy Root Certificate Authority \ + - G2\" \"fetch\" ] = false) do={\r\ + \n \$LogPrint warning \$0 (\"Downloading required certificate failed.\ + \");\r\ + \n :error false;\r\ + \n }\r\ + \n :local Data ([ /tool/fetch check-certificate=yes-without-crl output=\ + user http-method=post \\\r\ + \n (\"https://api.telegram.org/bot\" . \$TokenId . \"/sendMessage\") \ + \\\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) as-va\ + lue ]->\"data\");\r\ + \n :set (\$TelegramMessageIDs->[ :tostr ([ :deserialize from=json value\ + =\$Data ]->\"result\"->\"message_id\") ]) 1;\r\ + \n } do={\r\ + \n \$LogPrint info \$0 (\"Failed sending Telegram notification: \" . \$\ + Err . \" - Queuing...\");\r\ + \n :if ([ :typeof \$TelegramQueue ] = \"nothing\") do={:set TelegramQue\ + ue ({})}\r\ + \n :set Text (\$Text . \"\\n\" . [ \$SymbolForNotification \"alarm-cloc\ + k\" ] . \\\r\ + \n [ \$EscapeMD (\"This message was queued since _\" . [ /system/cloc\ + k/get date ] . \\\r\ + \n \" \" . [ /system/clock/get time ] . \"_ and may be obsolete.\") \ + \"plain\" \"_\" ]);\r\ + \n :set (\$TelegramQueue->[ :len \$TelegramQueue ]) { tokenid=\$TokenId\ + ;\r\ + \n http-data=(\$HTTPData . \"&text=\" . [ \$UrlEncode \$Text ]) };\r\ + \n :if ([ :len [ /system/scheduler/find where name=\"_FlushTelegramQueu\ + e\" ] ] = 0) do={\r\ + \n /system/scheduler/add name=\"_FlushTelegramQueue\" interval=1m sta\ + rt-time=startup \\\r\ + \n on-event=(\":global FlushTelegramQueue; \\\$FlushTelegramQueue;\ + \");\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n# purge the Telegram queue\r\ + \n:set PurgeTelegramQueue do={\r\ + \n :global TelegramQueue;\r\ + \n /system/scheduler/remove [ find where name=\"_FlushTelegramQueue\" ];\ + \r\ + \n :set TelegramQueue;\r\ + \n}\r\ + \n# send notification via telegram - expects at least two string arguments\ + \r\ + \n:set SendTelegram do={ :onerror Err {\r\ + \n :global SendTelegram2;\r\ + \n \$SendTelegram2 ({ origin=\$0; subject=\$1; message=\$2; link=\$3; sil\ + ent=\$4 });\r\ + \n} do={\r\ + \n :global ExitOnError; \$ExitOnError \$0 \$Err;\r\ + \n} }\r\ + \n# send notification via telegram - expects one array argument\r\ + \n:set SendTelegram2 do={\r\ + \n :local Notification \$1;\r\ + \n :global NotificationFunctions;\r\ + \n (\$NotificationFunctions->\"telegram\") (\"\\\$NotificationFunctions->\ + \\\"telegram\\\"\") \$Notification;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=testIfThenElseAndEitherOr owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source=":local ExitOK false;\r\ + \n:local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local ExitError;\r\ + \n#:local IfThenElse;\r\ + \n#:local EitherOr;\r\ + \n\r\ + \n:local one 11;\r\ + \n:local two 8;\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n:local Test; # [ :totime \"22s\" ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n\r\ + \n:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set Test [ \$EitherOr \$Test \"\" ];\r\ + \n:log warning (\"\\\$ Test = \" . \$Test);\r\ + \n\r\ + \n:set ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :local IfThenElse;\r\ + \n :if (\$ExitOK = \"false\") do={\r\ + \n :log error (\$Name [ \$IfThenElse ([ :pick \$Name 0 1 ] = \"\\\$\") \ + \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n\r\ + \n#:log warning ([ \$IfThenElse (\$one != 0) \$one \$two ]);\r\ + \n\r\ + \n:set ExitOK true;\r\ + \n\$ExitError \$ExitOK \$ScriptName \"Err test custom\";" +add comment="Update and Backup send notification to EMail" \ + dont-require-permissions=no name=AM-Backup-UpdateEmail owner=\ + AM-Backup-UpdateEmail policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="#\ + !rsc by Vados\r\ + \n# RouterOS script: AM-Backup-UpdateEmail\r\ + \n# Script comment: Update and Backup send notification to EMail\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version>=6.43.7\r\ + \n# requires device-mode, fetch\r\ + \n#\r\ + \n# Updated: 15/04/2025\r\ + \n# Website: https://github.com/beeyev\r\ + \n# Notification e-mail (Make sure you have configured Email settings in T\ + ools -> Email)\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n:local IfThenElse do={:if ([ :tostr \$1 ] = \"true\" || [ :tobool \$1 ] \ + = true) do={:return \$2}; :return \$3;}\r\ + \n:local EitherOr do={\r\ + \n :if ([ :typeof \$1 ] = \"num\") do={:if ((\$1 != 0) = true) do={:retur\ + n \$1} else={:return \$2};}\r\ + \n :if ([ :typeof \$1 ] = \"time\") do={:if ((\$1 > 0s) = true) do={:retu\ + rn \$1} else={:return \$2};}\r\ + \n :if ([ :len [ :tostr \$1 ] ] > 0) do={:return \$1} else={:return \$2};\ + \r\ + \n}\r\ + \n:local ExitError do={\r\ + \n :local ExitOK [ :tostr \$1 ];\r\ + \n :local Name [ :tostr \$2 ];\r\ + \n :local Error [ :tostr \$3 ];\r\ + \n :if (\$ExitOK = \"false\") do={:log error (\$Name [ \$IfThenElse ([ :p\ + ick \$Name 0 1 ] = \"\\\$\") \\\r\ + \n \"Function\" \"Script\" ] . \" '\" . \$Name . \"' exited with er\ + ror\" . \\\r\ + \n [ \$IfThenElse (!(\$Error ~ \"^(|true|false)\\\$\")) (\": \" . \ + \$Error) \".\" ]);}\r\ + \n}\r\ + \n\r\ + \n:global LogPrint;\r\ + \n:global EditSchedulers;\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n:local SchedInterval \"7d\";\r\ + \n:local SchedStartTime \"03:41:27\";\r\ + \n:local SchedComment \"Every 7 Day Check For Update and Backup send notif\ + ication to EMail.\";\r\ + \n\r\ + \n# Function to add or edit schedulers for scripts, to ensure they are set\ + \_up correctly\r\ + \n# How to use:\r\ + \n# Call this function with the following parameters:\r\ + \n# 1. \$1 - Scheduler name (usually same as script name) [*]\r\ + \n# 2. \$2 - Interval (e.g. \"1d\" (default is \"1d\")) [**]\r\ + \n# 3. \$3 - Start Time (e.g. \"00:00:00\", default is current time) [**]\ + \r\ + \n# 4. \$4 - Comment (optional, default: \"Scheduler for script: \")\ + \_[**]\r\ + \n# 5. \$5 - On Event command (e.g. \"/system/script { run ; }\" (de\ + fault is \"/system/script { run \$SchedName; }\")) [**]\r\ + \n# 6. \$6 - Policy (e.g. \"ftp,reboot,read,write,policy,test,password,sni\ + ff,sensitive,romon\" (default is the same)) [**]\r\ + \n# 7. \$7 - Start Date (e.g. \"jan/01/1970\", default is current date) [*\ + *]\r\ + \n# Notes:\r\ + \n# * - Scheduler name is required, and is used to identify the scheduler\ + \_to create or edit. It is usually the same as the script name (\$ScriptNa\ + me).\r\ + \n# ** - If optional parameters are not provided, default values will be u\ + sed.\r\ + \n# This allows for quick setup with minimal parameters, while still \ + providing flexibility for customization.\r\ + \n# Example usage:\r\ + \n# :if ([ \$EditSchedulers \"my-script\" ] = false) do={:set ExitOK false\ + ; :error true;}\r\ + \n# :if ([ \$EditSchedulers \"my-script\" \"1d\" \"03:41:27\" \"My custom \ + scheduler comment\" false false \"jan/01/1970\" ] = false) do={:set ExitOK\ + \_false; :error true;}\r\ + \n\r\ + \n:set EditSchedulers do={\r\ + \n :local SchedName [ :tostr \$1 ];\r\ + \n :if ([ :len \$SchedName ] = 0) do={ :return false; }\r\ + \n :local Interval [ :tostr \$2 ];\r\ + \n :if ([ :len \$Interval ] = 0) do={ :set Interval \"1d\"; }\r\ + \n :local StartTime [ :tostr \$3 ];\r\ + \n :if ([ :len \$StartTime ] = 0) do={ :set StartTime [/system/clock/get \ + time]; }\r\ + \n :local Comment [ :tostr \$4 ];\r\ + \n :if ([ :len \$Comment ] = 0) do={ :set Comment \"Scheduler for script:\ + \_\$SchedName\"; }\r\ + \n :local OnEvent [ :tostr \$5 ];\r\ + \n :if ([ :len \$OnEvent ] = 0) do={ :set OnEvent \"/system/script { run \ + \$SchedName; }\"; }\r\ + \n :local Policy [ :tostr \$6 ];\r\ + \n :if ([ :len \$Policy ] = 0) do={ :set Policy \"ftp,reboot,read,write,p\ + olicy,test,password,sniff,sensitive,romon\"; }\r\ + \n :local StartDate [ :tostr \$7 ];\r\ + \n :if ([ :len \$StartDate ] = 0) do={ :set StartDate [/system/clock/get \ + date]; }\r\ + \n\r\ + \n :onerror Err {\r\ + \n # Check if scheduler exists, if not - create it\r\ + \n :if ([ :len [ /system/scheduler/find where name=\$SchedName ] ] = 0)\ + \_do={\r\ + \n :log warning (\$SchedName . \"System Scheduler for script: \" . \$\ + SchedName . \" is NOT SET! Fix It Now!\");\r\ + \n /system/scheduler/add comment=\$Comment interval=\$Interval name=\ + \$SchedName on-event=\$OnEvent policy=\$Policy start-date=\$StartDate star\ + t-time=\$StartTime;\r\ + \n }\r\ + \n # Check if scheduler parameters are correct, if not - fix them\r\ + \n :if ([ /system/scheduler/get \$SchedName comment ] != \$Comment) do=\ + {\r\ + \n /system/scheduler/set \$SchedName comment=\$Comment;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName interval ] != \$Interval) d\ + o={\r\ + \n /system/scheduler/set \$SchedName interval=\$Interval;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName on-event ] != \$OnEvent) do\ + ={\r\ + \n /system/scheduler/set \$SchedName on-event=\$OnEvent;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName policy ] != \$Policy) do={\ + \r\ + \n /system/scheduler/set \$SchedName policy=\$Policy;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-date ] != \$StartDate\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-date=\$StartDate;\r\ + \n }\r\ + \n :if ([ /system/scheduler/get \$SchedName start-time ] != \$StartTime\ + ) do={\r\ + \n /system/scheduler/set \$SchedName start-time=\$StartTime;\r\ + \n }\r\ + \n } do={:log error (\$SchedName . \"Failed to add/modify scheduler for '\ + \" . \$SchedName . \"' failed: \" . \$Err); :return false};\r\ + \n :return true;\r\ + \n}\r\ + \n\r\ + \n:if ([ \$EditSchedulers \$ScriptName \$SchedInterval \$SchedStartTime \$\ + SchedComment ] = false) do={\r\ + \n :log error (\$ScriptName . \"Failed to add/modify scheduler for \" . \ + \$ScriptName . \"!\");\r\ + \n :set ExitOK true;\r\ + \n :error true;\r\ + \n}\r\ + \n\r\ + \n# Local Function CheckOSUpdateMode\r\ + \n:local CheckOSUpdateMode;\r\ + \n:set CheckOSUpdateMode do={\r\ + \n :local NewMode [ :tostr \$1 ];\r\ + \n :if ([ :len \$NewMode ] = 0) do={\r\ + \n :log error (\$0 . \"/nFunction /\$CheckOSUpdateMode received empty v\ + alue!\");\r\ + \n :return false;\r\ + \n :error true;\r\ + \n }\r\ + \n\r\ + \n :global BackupAndUpdateMode;\r\ + \n :global OldMode [ :tostr \$BackupAndUpdateMode ];\r\ + \n :if (\$NewMode != \$BackupAndUpdateMode && \$NewMode = \"updateOnce\")\ + \_do={\r\ + \n :set BackupAndUpdateMode [ :tostr \$NewMode ];\r\ + \n :log info (\$0 . \"/nOk. /\$BackupAndUpdateMode set to: '\" . \$NewM\ + ode . \"'.\\nYou're a good boy! ))\\nI'm launching the update in 5 seconds\ + .\");\r\ + \n :delay 5;\r\ + \n /system/script { run \$ScriptName; };\r\ + \n } else={\r\ + \n :log warning (\$0 . \"/nFunction /\$CheckOSUpdateMode received value: \ + '\" . \$NewMode . \"', but is may be only \\\"updateOnce\\\"!\");\r\ + \n :return false;\r\ + \n }\r\ + \n :return true;\r\ + \n :error false;\r\ + \n}\r\ + \n\r\ + \n# :if ([ :len [ /system/scheduler/find where name=\"running-from-backup\ + -partition\" ] ] > 0) do={\r\ + \n# \$LogPrint warning \$ScriptName (\"Running from backup partition, r\ + efusing to act.\");\r\ + \n# :set ExitOK true;\r\ + \n# :error false;\r\ + \n# }\r\ + \n\r\ + \n:global EmailGeneralTo;\r\ + \n:global BackupAndUpdateMode;\r\ + \n:global forceBackup;\r\ + \n:global BackupPassword;\r\ + \n:global BackupSens;\r\ + \n:global updateChannel;\r\ + \n\r\ + \n:global DevInfoIncludeIP;\r\ + \n\r\ + \n# Installs patch updates only (BackupAndUpdateMode = \"osupdate\").\r\ + \n# Works for `stable` and `long-term` channels.\r\ + \n# Updates only if MAJOR.MINOR match (e.g. 6.43.2 > 6.43.6 allowed, 6.44.\ + 1 skipped).\r\ + \n# Sends info if a newer (non-patch) version is found.\r\ + \n:local installOnlyPatchUpdates false;\r\ + \n:local scriptVersion \"26.04.30\";\r\ + \n:local copyrightUrl \"https://www.github.com/vados-dev/\";\r\ + \n#Script messages prefix\r\ + \n:local SMP \"BkpAndUpdate:\";\r\ + \n\r\ + \n:local exitErrorMessage \"\$SMP script stopped due to an error. Please c\ + heck logs for more details.\";\r\ + \n:log info \"\\n\\n\$SMP Script \\\"Mikrotik RouterOS automatic backup & \ + update\\\" v.\$scriptVersion started.\";\r\ + \n:log info \"\$SMP Script Mode: `\$BackupAndUpdateMode`, Update channel: \ + `\$updateChannel`, Force backup: `\$forceBackup`, Install only patch updat\ + es: `\$installOnlyPatchUpdates`\";\r\ + \n\r\ + \n## vv FUNCTIONS vv ##\r\ + \n:global WaitCheckUpdates; \$WaitCheckUpdates;\r\ + \n:global MiniDateTimeStamp;\r\ + \n:local MDTSFix [\$MiniDateTimeStamp];\r\ + \n:global Identity;\r\ + \n:global IdentityShort;\r\ + \n:global devBoardName;\r\ + \n:global runningVersion;\r\ + \n:global runningChannel;\r\ + \n:global devModel;\r\ + \n:global devRbSerialNumber;\r\ + \n:global ROsVerAvail;\r\ + \n:global PkgUpdStatus;\r\ + \n\r\ + \n# Checks if two RouterOS version strings differ only by the patch versio\ + n\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.2.4\"] # Output: true\r\ + \n# :put [\$FuncIsPatchUpdateOnly \"6.2.1\" \"6.3.1\"] # Output: false\r\ + \n:local FuncIsPatchUpdateOnly do={\r\ + \n :local ver1 \$1;\r\ + \n :local ver2 \$2;\r\ + \n # Extract the major and minor components from a version\r\ + \n :local extractMajorMinor do={\r\ + \n :local ver \$1;\r\ + \n :local dot1 [:find \$ver \".\"];\r\ + \n :if (\$dot1 = -1) do={:return \$ver}\r\ + \n :local major [:pick \$ver 0 \$dot1];\r\ + \n :local rest [:pick \$ver (\$dot1 + 1) [:len \$ver]];\r\ + \n :local dot2 [:find \$rest \".\"];\r\ + \n :local minor \$rest;\r\ + \n :if (\$dot2 >= 0) do={:set minor [:pick \$rest 0 \$dot2]}\r\ + \n :return (\$major . \".\" . \$minor);\r\ + \n }\r\ + \n\r\ + \n# Compare the major and minor components of both version strings\r\ + \n:if ([\$extractMajorMinor \$ver1] = [\$extractMajorMinor \$ver2]) do={:r\ + eturn true}\r\ + \n :return false;\r\ + \n}\r\ + \n# Creates backups and returns array of names\r\ + \n# Possible arguments:\r\ + \n# \$1 - file name, without extension\r\ + \n# \$2 - password (optional)\r\ + \n# \$3 - sensitive data in config (optional, default: false)\r\ + \n# Example:\r\ + \n#:put [\$FuncCreateBackups \$backupName]\r\ + \n:local FuncCreateBackups do={\r\ + \n :local backupName [ :tostr \$1 ];\r\ + \n :local BackPass \$2;\r\ + \n :local BackSens \$3;\r\ + \n\r\ + \n #Script messages prefix\r\ + \n :local SMP \"BkpAndUpdate:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` started, input:\ + \_`\$backupName`\");\r\ + \n\r\ + \n # validate required parameter: backupName\r\ + \n :if ([:typeof \$backupName] != \"str\" or [:len \$backupName] = 0) do=\ + {\r\ + \n :log error \"\$SMP parameter 'backupName' is required and must be a \ + non-empty string\";\r\ + \n# :log warning \"\$SMP parameter '\\\$backupName' is required and mus\ + t be a non-empty string.\\nI set '\\\$backupName' to 'default-backup'\";\r\ + \n# :set \$backupName \"default-backup\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n\r\ + \n :local backupFileSys \"\$backupName.backup\";\r\ + \n :local backupFileConfig \"\$backupName.rsc\";\r\ + \n :local backupNames {\$backupFileSys;\$backupFileConfig};\r\ + \n\r\ + \n ## Perform system backup\r\ + \n :if ([:len \$BackPass] = 0) do={\r\ + \n :log info (\"\$SMP starting backup without password, backup name: `\ + \$backupName`\");\r\ + \n /system backup save dont-encrypt=yes name=\$backupName;\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting backup with password, backup name: `\$ba\ + ckupName`\");\r\ + \n /system backup save password=\$BackPass name=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP system backup created: `\$backupFileSys`\");\r\ + \n ## Export config file\r\ + \n :if (\$BackSens = true) do={\r\ + \n :log info (\"\$SMP starting export config with sensitive data, backu\ + p name: `\$backupName`\");\r\ + \n # Since RouterOS v7 it needs to be explicitly set that we want to ex\ + port sensitive data\r\ + \n :if ([:pick [/system resource get version] 0 1] < 7) do={\r\ + \n :execute \"/export compact terse file=\$backupName\";\r\ + \n } else={\r\ + \n :execute \"/export compact show-sensitive terse file=\$backupName\ + \";\r\ + \n }\r\ + \n } else={\r\ + \n :log info (\"\$SMP starting export config without sensitive data, ba\ + ckup name: `\$backupName`\");\r\ + \n /export compact hide-sensitive terse file=\$backupName;\r\ + \n }\r\ + \n :log info (\"\$SMP Config export complete: `\$backupFileConfig`\");\r\ + \n :log info (\"\$SMP Waiting a little to ensure backup files are written\ + \");\r\ + \n :delay 20;\r\ + \n :if ([:len [/file find name=\$backupFileSys]] > 0) do={\r\ + \n :log info (\"\$SMP system backup file successfully saved to the file\ + \_system: `\$backupFileSys`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP system backup was not created, file does not exi\ + st: `\$backupFileSys`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :if ([:len [/file find name=\$backupFileConfig]] > 0) do={\r\ + \n :log info (\"\$SMP config backup file successfully saved to the file\ + \_system: `\$backupFileConfig`\");\r\ + \n } else={\r\ + \n :log error (\"\$SMP config backup was not created, file does not exi\ + st: `\$backupFileConfig`\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info (\"\$SMP global function `FuncCreateBackups` finished. Creat\ + ed backups, system: `\$backupFileSys`, config: `\$backupFileConfig`\")\r\ + \n :return \$backupNames;\r\ + \n}\r\ + \n# Sends an email\r\ + \n# Parameters:\r\ + \n# \$1 - to (email address)\r\ + \n# \$2 - subject\r\ + \n# \$3 - body\r\ + \n# \$4 - file attachments (optional; pass \"\" if not needed)\r\ + \n#\r\ + \n# Example:\r\ + \n# \$FuncSendEmailSafe \"admin@domain.com\" \"Backup Done\" \"Backup comp\ + lete.\" \"backup1.backup\"\r\ + \n:local FuncSendEmailSafe do={\r\ + \n :global EmailGeneralTo;\r\ + \n :local emailSubject \$2;\r\ + \n :local emailBody \$3;\r\ + \n :local emailAttachments \$4;\r\ + \n :local SMP \"Bkp&Upd:\";\r\ + \n :local exitErrorMessage \"\$SMP script stopped due to an error. Please\ + \_check logs for more details.\";\r\ + \n :log info \"\$SMP Attempting to send email to `\$EmailGeneralTo`\";\r\ + \n # SAFETY: wait for any previously queued email to finish\r\ + \n :local waitTimeoutPre 60;\r\ + \n :local waitCounterPre 0;\r\ + \n :while (([/tool e-mail get last-status] = \"resolving-dns\" or [/tool \ + e-mail get last-status] = \"in-progress\")) do={\r\ + \n :if (\$waitCounterPre >= \$waitTimeoutPre) do={\r\ + \n :log error \"\$SMP Email send aborted: previous send did not compl\ + ete after \$waitTimeoutPre seconds\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n :log info \"\$SMP Waiting for previous email to finish (status: \$[/\ + tool e-mail get last-status])...\";\r\ + \n :delay 1; \r\ + \n :set waitCounterPre (\$waitCounterPre + 1);\r\ + \n }\r\ + \n # Send the email\r\ + \n :do {\r\ + \n /tool e-mail send to=\$EmailGeneralTo subject=\$emailSubject body=\$\ + emailBody file=\$emailAttachments;\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Email send command failed to execute. Check logs \ + and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n # Wait for send status to change from \"in-progress\" / \"resolving-dn\ + s\"\r\ + \n :local waitTimeout 60;\r\ + \n :local waitCounter 0;\r\ + \n :local emailStatus \"\";\r\ + \n :log info \"\$SMP Waiting for email to be sent, timeout in `\$waitTime\ + out` seconds...\";\r\ + \n :while (\$waitCounter < \$waitTimeout) do={\r\ + \n :set emailStatus [/tool e-mail get last-status];\r\ + \n :if (\$emailStatus != \"in-progress\" and \$emailStatus != \"resolvi\ + ng-dns\") do={\r\ + \n :log info \"\$SMP Email send status received: \$emailStatus\";\r\ + \n # exit loop\r\ + \n :set waitCounter \$waitTimeout;\r\ + \n } else={:delay 1; :set waitCounter (\$waitCounter + 1)}\r\ + \n }\r\ + \n # Final decision based on last status\r\ + \n :if (\$emailStatus = \"succeeded\") do={\r\ + \n :log info \"\$SMP Email successfully sent to `\$EmailGeneralTo`\";\ + \r\ + \n } else={\r\ + \n :log error \"\$SMP Email failed to send. Status: `\$emailStatus`. Ch\ + eck logs for more details and verify email settings.\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n# Global variable to track current update step\r\ + \n# They need to be initialized here first to be available in the script\r\ + \n:global buGlobalVarTargetOsVersion;\r\ + \n:global buGlobalVarScriptStep;\r\ + \n:local scriptStep \$buGlobalVarScriptStep;\r\ + \n:do {\r\ + \n /system/script/environment remove buGlobalVarScriptStep;\r\ + \n} on-error={}\r\ + \n:if ([:len \$scriptStep] = 0) do={\r\ + \n :set scriptStep 1;\r\ + \n}\r\ + \n## ^^ FUNCTIONS ^^ ##\r\ + \n#\r\ + \n# Initial validation\r\ + \n## Check email settings\r\ + \n:if ([:len \$EmailGeneralTo] < 3) do={\r\ + \n :log error (\"\$SMP Parameter `\\\$EmailGeneralTo` is not set, or cont\ + ains invalid value. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Values will be defined later in the script\r\ + \n:local emailServer \"\";\r\ + \n:local emailFromAddress [/tool e-mail get from];\r\ + \n:log info \"\$SMP Validating email settings...\";\r\ + \n:do {\r\ + \n :set emailServer [/tool e-mail get server];\r\ + \n} on-error={\r\ + \n # This is a workaround for the RouterOS v7.12 and older versions\r\ + \n :set emailServer [/tool e-mail get address];\r\ + \n}\r\ + \n:if (\$emailServer = \"0.0.0.0\") do={\r\ + \n :log error (\"\$SMP Email server address is not correct: `\$emailServe\ + r`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n:if ([:len \$emailFromAddress] < 3) do={\r\ + \n :log error (\"\$SMP Email configuration FROM address is not correct: `\ + \$emailFromAddress`, check `Tools -> Email`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Script mode validation\r\ + \n:if (\$BackupAndUpdateMode != \"backup\" and \$BackupAndUpdateMode != \"\ + osupdate\" and \$BackupAndUpdateMode != \"updateOnce\" and \$BackupAndUpda\ + teMode != \"osnotify\") do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$BackupAndUpdateMode` is not \ + set, or contains invalid value: `\$BackupAndUpdateMode`. Script stopped.\"\ + );\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Update channel validation\r\ + \n:if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\" \ + and \$updateChannel != \"testing\" and \$updateChannel != \"development\")\ + \_do={\r\ + \n :log error (\"\$SMP Script parameter `\\\$updateChannel` is not set, o\ + r contains invalid value: `\$updateChannel`. Script stopped.\");\r\ + \n :error \$exitErrorMessage;\r\ + \n}\r\ + \n# Verify if script is set to install patch updates and if the update cha\ + nnel is valid\r\ + \n:if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \"\ + updateOnce\") and \$installOnlyPatchUpdates = true) do={\r\ + \n :if (\$updateChannel != \"stable\" and \$updateChannel != \"long-term\ + \") do={\r\ + \n :log error (\"\$SMP Patch-only updates enabled, but update channel `\ + \$updateChannel` is invalid. Only `stable` and `long-term` are supported. \ + Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n \r\ + \n :if (\$runningChannel != \"stable\" and \$runningChannel != \"long-ter\ + m\") do={\r\ + \n :log error (\"\$SMP Script is set to install only patch updates, but\ + \_the installed RouterOS version is not from `stable` or `long-term` chann\ + el: `\$runningChannel`. Script stopped\");\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n}\r\ + \n#\r\ + \n\r\ + \n:local rawTime [/system clock get time];\r\ + \n:local rawDate [/system clock get date];\r\ + \n\r\ + \n:local deviceOsVerAndChannelRunning [/system/resource/get version];\r\ + \n\r\ + \n:local backupNameTemplate (\"backup_v\" . \$runningVersion . \"_\" .\ + \_\$runningChannel . \"_\" . \$MDTSFix);\r\ + \n:local backupNameBeforeUpdate (\$backupNameTemplate . \"_before_update\"\ + );\r\ + \n:local backupNameAfterUpdate (\$backupNameTemplate . \"_after_update\")\ + ;\r\ + \n\r\ + \n## Email body template\r\ + \n:local mailSubjectPrefix \"\$SMP Device - `\$IdentityShort`\";\r\ + \n:local mailBodyCopyright \"Mikrotik RouterOS automatic backup & update \ + (ver. \$scriptVersion) \\n\$copyrightUrl\";\r\ + \n:local changelogUrl \"Check RouterOS changelog: https://mikrotik.com\ + /download/changelogs/\";\r\ + \n:local mailBodyDeviceInfo \"\";\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"Device information:\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\n-------------------\ + --\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nName: \$Identity\")\ + ;\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nModel: \$devModel\"\ + );\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBoard: \$devBoardNa\ + me\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nSerial number: \$de\ + vRbSerialNumber\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterOS version: v\ + \$deviceOsVerAndChannelRunning\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nBuild time: \$[/sys\ + tem/resource/get build-time]\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nRouterboard FW Avai\ + lable: \$ROsVerAvail\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nDevice date-time: \ + \$rawDate \$rawTime (\$[/system/clock/get time-zone-name ])\");\r\ + \n:set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nUptime: \$[/system/\ + resource/get uptime]\");\r\ + \n## IP address detection\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local PublicIpAddress [ \$GetPubIp ];\r\ + \n :set mailBodyDeviceInfo (\$mailBodyDeviceInfo . \"\\nPublic IP address\ + : \" . \$PublicIpAddress . \".\");\r\ + \n :log info \"\$SMP Public IP address detected: \$PublicIpAddress\";\r\ + \n} \r\ + \n\r\ + \n:local mailAttachments [:toarray \"\"];\r\ + \n:if (\$scriptStep = 1 or \$scriptStep = 3) do={\r\ + \n :if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP Waiting for one minute before continuing to the f\ + inal step.\");\r\ + \n :delay 1m;\r\ + \n }\r\ + \n}\r\ + \n\r\ + \n## STEP 1: Create backups, check for new RouterOS, and send email\r\ + \n## Steps 2\C3\A23 run only if auto-update is enabled and a new version i\ + s available\r\ + \n:if (\$scriptStep = 1) do={\r\ + \n :global BackupPassword;\r\ + \n :global BackupSens;\r\ + \n :local isNewOsUpdateAvailable false;\r\ + \n :local isLatestOsAlreadyInstalled true;\r\ + \n :local isOsNeedsToBeUpdated false;\r\ + \n :local isUpdateCheckSucceeded false;\r\ + \n :local isEmailNeedsToBeSent false;\r\ + \n :local mailSubjectPartAction \"\";\r\ + \n :local mailPtBodyAction \"\";\r\ + \n :local mailPtSubjectBackup \"\";\r\ + \n :local mailPtBodyBackup \"\";\r\ + \n # Checking for new version\r\ + \n :if (\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"osnotify\" or \$BackupAndUpdateMode = \"updateOnce\") do={\r\ + \n :log info (\"\$SMP Setting update channel to `\$updateChannel`\");\r\ + \n /system/package/update/set channel=\$updateChannel;\r\ + \n :log info (\"\$SMP Checking for new RouterOS version. Current instal\ + led version is: `\$runningVersion`\");\r\ + \n \$LogPrint debug \$ScriptName (\"Checking for updates...\");\r\ + \n \r\ + \n# /system/package/update/check-for-updates without-paging as-value;\ + \r\ + \n# /system/package/update check-for-updates;\r\ + \n :delay 5s;\r\ + \n :set PkgUpdStatus [ /system/package/update/get status ];\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :set ROsVerAvail [/system/package/update/get latest-version];\r\ + \n }\r\ + \n\r\ + \n :if (\$PkgUpdStatus = \"New version is available\") do={\r\ + \n :log info (\"\$SMP New RouterOS version is available: `\$ROsVerAva\ + il`\");\r\ + \n :set isNewOsUpdateAvailable true;\r\ + \n :set isLatestOsAlreadyInstalled false;\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"New RouterOS available\";\r\ + \n :set mailPtBodyAction \"New RouterOS version is available, curren\ + t version: v\$runningVersion, new version: v\$ROsVerAvail. \\n\$changelogU\ + rl\";\r\ + \n } else={\r\ + \n :if (\$PkgUpdStatus = \"System is already up to date\") do={\r\ + \n :log info (\"\$SMP No new RouterOS version is available, the lat\ + est version is already installed: `v\$runningVersion`\");\r\ + \n :set isUpdateCheckSucceeded true;\r\ + \n :set mailSubjectPartAction \"No os update available\";\r\ + \n :set mailPtBodyAction \"No new RouterOS version is available, t\ + he latest version is already installed: `v\$runningVersion`\";\r\ + \n } else={\r\ + \n :log error (\"\$SMP Failed to check for new RouterOS version. Pa\ + ckage check status: `\$PkgUpdStatus`\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :set mailSubjectPartAction \"Error unable to check new os versio\ + n\";\r\ + \n :set mailPtBodyAction \"An error occurred while checking for a \ + new RouterOS version.\\nStatus returned: `\$PkgUpdStatus`\\n\\nPlease revi\ + ew the logs on the device for more details and verify internet connectivit\ + y.\";\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n # Checking if the script needs to install new os version\r\ + \n :if ((\$BackupAndUpdateMode = \"osupdate\" or \$BackupAndUpdateMode = \ + \"updateOnce\") and \$isNewOsUpdateAvailable = true) do={\r\ + \n :if (\$installOnlyPatchUpdates = true) do={\r\ + \n :if ([\$FuncIsPatchUpdateOnly \$runningVersion \$ROsVerAvail] = tr\ + ue) do={\r\ + \n :log info \"\$SMP New RouterOS version is available, and it is a\ + \_patch update. Current version: v\$runningVersion, new version: v\$ROsVer\ + Avail\";\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n } else={\r\ + \n :log info \"\$SMP The script will not install this update, becau\ + se it is not a patch update. Current version: v\$runningVersion, new versi\ + on: v\$ROsVerAvail\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThis update wil\ + l not be installed, because the script is set to install only patch update\ + s.\");\r\ + \n }\r\ + \n } else={\r\ + \n :set isOsNeedsToBeUpdated true;\r\ + \n }\r\ + \n }\r\ + \n # Checking If the script needs to create a backup\r\ + \n :if (\$forceBackup = true or \$BackupAndUpdateMode = \"backup\" or \$i\ + sOsNeedsToBeUpdated = true) do={\r\ + \n :log info (\"\$SMP Starting backup process.\");\r\ + \n :set isEmailNeedsToBeSent true;\r\ + \n :local backupName \$backupNameTemplate;\r\ + \n # This means it's the first step where we create a backup before the\ + \_update process\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :set backupName \$backupNameBeforeUpdate;\r\ + \n #Email body if the purpose of the script is to update the device\r\ + \n :set mailSubjectPartAction \"Update preparation\";\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nThe update proces\ + s for device '\$Identity' is scheduled to upgrade RouterOS from version v.\ + \$runningVersion to version v.\$ROsVerAvail (Update channel: \$updateChann\ + el)\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nPlease note: The \ + update will proceed only after a successful backup.\");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nA final report wi\ + th detailed information will be sent once the update process is completed.\ + \");\r\ + \n :set mailPtBodyAction (\$mailPtBodyAction . \"\\nIf you do not rec\ + eive a second email within the next 10 minutes, there may be an issue. Ple\ + ase check your device logs for further information.\");\r\ + \n }\r\ + \n :do {\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupName \$BackupPas\ + sword \$BackupSens];\r\ + \n :set mailPtSubjectBackup \"Backup created\";\r\ + \n :set mailPtBodyBackup \"System backups have been successfully crea\ + ted and attached to this email.\";\r\ + \n } on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :set mailPtSubjectBackup \"Backup failed\";\r\ + \n :set mailPtBodyBackup \"The script failed to create backups. Pleas\ + e check device logs for more details.\";\r\ + \n :log warning \"\$SMP Backup creation failed. Update process will b\ + e canceled if automatic update is enabled\";\r\ + \n }\r\ + \n }\r\ + \n :if (\$isEmailNeedsToBeSent = true) do={\r\ + \n :log info \"\$SMP Preparing to send email...\";\r\ + \n :local mailStep1Subject \$mailSubjectPrefix;\r\ + \n :local mailStep1Body \"\";\r\ + \n # subject\r\ + \n :if (\$mailSubjectPartAction != \"\") do={:set mailStep1Subject (\$\ + mailStep1Subject . \" - \" . \$mailSubjectPartAction)}\r\ + \n :if (\$mailPtSubjectBackup != \"\") do={:set mailStep1Subject (\$ma\ + ilStep1Subject . \" - \" . \$mailPtSubjectBackup)}\r\ + \n # body\r\ + \n :if (\$mailPtBodyAction != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyAction . \"\\n\\n\")}\r\ + \n :if (\$mailPtBodyBackup != \"\") do={:set mailStep1Body (\$mailStep1\ + Body . \$mailPtBodyBackup . \"\\n\\n\")}\r\ + \n :set mailStep1Body (\$mailStep1Body . \$mailBodyDeviceInfo . \"\\n__\ + ________\\n\" . \$mailBodyCopyright);\r\ + \n :set mailStep1Body (\$mailStep1Body . \"\\n[*** TEST START ***]\\n\\\ + \$DeviceInfo: \" . [ \$DeviceInfo ] . \"\\n[*** TEST END ***]\\n\");\r\ + \n # Send email with backups\r\ + \n :do {\$FuncSendEmailSafe \$EmailGeneralTo \$mailStep1Subject \$mailS\ + tep1Body \$mailAttachments} on-error={\r\ + \n :set isOsNeedsToBeUpdated false;\r\ + \n :log error \"\$SMP The script will not proceed with the update pro\ + cess, because the email was not sent.\";\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2s;\r\ + \n }\r\ + \n :if (\$isOsNeedsToBeUpdated = true) do={\r\ + \n :global OldMode;\r\ + \n :log info \"\$SMP everything is ready to install new RouterOS, going\ + \_to start the update process and reboot the device.\";\r\ + \n :do {\r\ + \n :local nextStep 2;\r\ + \n :if (\$isCloudHostedRouter = true) do={\r\ + \n :log info \"\$SMP The device is a cloud hosted router, the secon\ + d step updating the Routerboard firmware will be skipped.\";\r\ + \n :set nextStep 3;\r\ + \n }\r\ + \n :local scheduledCommand (\":delay 5s; /system/scheduler remove BKP\ + UPD-NEXT-BOOT-TASK; \\\r\ + \n :global buGlobalVarScriptStep \$nextStep; :global buGlobalVarTarg\ + etOsVersion \\\"\$ROsVerAvail\\\"; \\\r\ + \n :delay 10s; /system/script { run \$ScriptName; }\");\r\ + \n /system/scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\$schedu\ + ledCommand start-time=startup interval=0;\r\ + \n /system/package/update install;\r\ + \n } on-error={\r\ + \n # Failed to install new os version, remove the task and variables\ + \r\ + \n :do {\r\ + \n /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\r\ + \n :set BackupAndUpdateMode \$OldMode;\r\ + \n :delay 2;\r\ + \n :do {\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \"ERROR Remove\ + \_global variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n# :if ([ :len \$OldMode ] > 0) do={\r\ + \n# :do {\r\ + \n# :global OldMode (a);\r\ + \n# } on-error={\r\ + \n# :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Re\ + move global variable \\\$OldMode failed!\");\r\ + \n# }\r\ + \n# }\r\ + \n } on-error={\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Pleas\ + e check device logs for more details. \\\r\ + \n \\nAnd Failed to remove task and variables! Check it!\";\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove \ + task and global variables!\");\r\ + \n }\r\ + \n :log error \"\$SMP Failed to install new RouterOS version. Please \ + check device logs for more details.\";\r\ + \n :local mailUpdateErrorSubject (\$mailSubjectPrefix . \" - Update f\ + ailed\");\r\ + \n :local mailUpdateErrorBody \"The script was unable to install new \ + RouterOS version. Please check device logs for more details.\";\r\ + \n # Send email with error\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailUpdateErrorSubject \$ma\ + ilUpdateErrorBody \"\";\r\ + \n :error \$exitErrorMessage;\r\ + \n }\r\ + \n }\r\ + \n}\r\ + \n## STEP 2: (Post-reboot) Upgrade RouterBOARD firmware\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und\r\ + \n:if (\$scriptStep = 2) do={\r\ + \n :log info \"\$SMP The script is in the second step, updating Routerboa\ + rd firmware.\";\r\ + \n :log info \"\$SMP Upgrading routerboard firmware from v.\$deviceRbCurr\ + entFw to v.\$deviceRbUpgradeFw\";\r\ + \n /system routerboard upgrade;\r\ + \n :delay 2;\r\ + \n :log info \"\$SMP routerboard upgrade process was completed, going to \ + reboot in a moment!\";\r\ + \n ## Set task to send final report on the next boot\r\ + \n /system scheduler add name=BKPUPD-NEXT-BOOT-TASK on-event=\":delay 2; \ + :global buGlobalVarScriptStep 3; \\\r\ + \n :global buGlobalVarTargetOsVersion \\\"\$buGlobalVarTargetOsVersion\\\ + \"; :delay 10s; /system/script { run \$ScriptName; }; \\ \r\ + \n :delay 5s; /system/scheduler remove BKPUPD-NEXT-BOOT-TASK;\" start-ti\ + me=startup interval=0;\r\ + \n :delay 2;\r\ + \n /system reboot;\r\ + \n}\r\ + \n\r\ + \n## STEP 3: Final report (after second reboot, with delay).\r\ + \n## Runs only if auto-update is enabled and a new RouterOS version was fo\ + und.\r\ + \n:if (\$scriptStep = 3) do={\r\ + \n :log info (\"\$SMP The script is in the third step, sending final repo\ + rt.\");\r\ + \n :local targetOsVersion \$buGlobalVarTargetOsVersion;\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"buGlobalVarTargetOsV\ + ersion\"];\r\ + \n :global buGlobalVarTargetOsVersion (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR Remove glob\ + al variable \\\$buGlobalVarTargetOsVersion failed!\");\r\ + \n }\r\ + \n\r\ + \n :if ([ :len \$OldMode ] != 0) do={\r\ + \n :do {\r\ + \n# /system/script/environment remove [find name=\"OldMode\"];\r\ + \n :global OldMode (a);\r\ + \n } on-error={\r\ + \n :set \$mailSubjectPrefix (\$mailSubjectPrefix . \" ERROR! Remove\ + \_global variable \\\$OldMode failed!\");\r\ + \n }\r\ + \n }\r\ + \n :if ([:len \$targetOsVersion] = 0) do={\r\ + \n :log warning \"\$SMP Something is wrong, the script was unable to ge\ + t the target updated OS version from the global variable.\";\r\ + \n }\r\ + \n :local mailStep3Subject \$mailSubjectPrefix;\r\ + \n :local mailStep3Body \"\";\r\ + \n :if (\$targetOsVersion = \$runningVersion) do={\r\ + \n :log info \"\$SMP Successfully verified new RouterOS version: target\ + : `\$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update completed - \ + Backup created\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"RouterOS and routerboard upg\ + rade process was completed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\nNew RouterOS version: v.\ + \$targetOsVersion, routerboard firmware: v.\$deviceRbCurrentFw\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"\\n\$changelogUrl\\nBackups \ + of the upgraded system are in the attachment of this email.\\n\\n\$mailBod\ + yDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n :set mailAttachments [\$FuncCreateBackups \$backupNameAfterUpdate \$\ + BackupPassword \$BackupSens];\r\ + \n } else={\r\ + \n :log error \"\$SMP Failed to verify new RouterOS version: target: `\ + \$targetOsVersion`, current: `\$runningVersion`\";\r\ + \n :set mailStep3Subject (\$mailStep3Subject . \" - Update failed\");\r\ + \n :set mailStep3Body (\$mailStep3Body . \"The script was unable to ver\ + ify that the new RouterOS version was installed, target version: `\$target\ + OsVersion`, current version: `\$runningVersion`\\nCheck device logs for mo\ + re details.\\n\\n\$mailBodyDeviceInfo\\n\\n\$mailBodyCopyright\");\r\ + \n }\r\ + \n \$FuncSendEmailSafe \$EmailGeneralTo \$mailStep3Subject \$mailStep3Bod\ + y \$mailAttachments;\r\ + \n :if ([:len \$mailAttachments] > 0) do={\r\ + \n :log info \"\$SMP Cleaning up backup files from the file system...\"\ + ;\r\ + \n /file remove \$mailAttachments;\r\ + \n :delay 2;\r\ + \n }\r\ + \n :log info (\"\$SMP Final report email sent successfully, and the scrip\ + t has finished.\");\r\ + \n}\r\ + \n:log info (\"\$SMP the script has finished, script step: `\$scriptStep` \ + \\n\\n\");\r\ + \n\r\ + \n} do={:global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err}\r\ + \n" +add dont-require-permissions=no name=testGetPubIP owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + global DevInfoIncludeIP;\r\ + \n:global CharacterReplace;\r\ + \n:global EscapeForRegEx;\r\ + \n:global UrlEncode;\r\ + \n\r\ + \n:if ([ \$DevInfoIncludeIP ] = true) do={\r\ + \n :global GetPubIp;\r\ + \n :local Char (\"\\n\\r\");\r\ + \n :local PublicIpAddress [\$GetPubIp];\r\ + \n# :if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 2) ] = \ + \"\\n\") do={:error true}; :set error falseon-error={:set PublicIpAddress \ + (\$PublicIpAddress . Char)};\r\ + \n#:if ([ :pick \$PublicIpAddress ([ :len \$PublicIpAddress ] - 1) ] = \"/\ + n\") do={\r\ + \n#:log warning \"N is = //n\"; \r\ + \n# :set PublicIpAddress [ \$CharacterReplace \$PublicIpAddress \$Char \"\ + \" ];\r\ + \n#}\r\ + \n# :set PublicIpAddress [ \$UrlEncode \$PublicIpAddress ];\r\ + \n#:local IpLen ([ :len \$PublicIpAddress ] -1);\r\ + \n#:set PublicIpAddress [:pick \$PublicIpAddress 0 \$IpLen];\r\ + \n :log warning \"Public IP address detected: \$PublicIpAddress , len = \ + \$IpLen\";\r\ + \n}\r\ + \n" +add comment="download, import and update firewall address-lists" \ + dont-require-permissions=no name=AM-FwAddrLists owner=AM-FwAddrLists \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="#!rsc by Vados\r\ + \n# RouterOS script: AM-FwAddrLists\r\ + \n# Script comment: download, import and update firewall address-lists\r\ + \n# Copyright (c) 2007-2026 Vados \r\ + \n#\r\ + \n#\r\ + \n# requires RouterOS, version=7.19\r\ + \n# https://ros.vados.ru/doc/fw-addr-lists.md\r\ + \n:local ExitOK false;\r\ + \n:onerror Err {\r\ + \n :global GlobalConfReady; :global GlobalFuncReady;\r\ + \n :retry { :if (\$GlobalConfReady != true || \$GlobalFuncReady != true) \ + \\\r\ + \n do={ :error (\"Global config and/or functions not ready.\"); }; } \ + delay=500ms max=50;\r\ + \n :local ScriptName [ :jobname ];\r\ + \n\r\ + \n :global GlobalScriptsUrl;\r\ + \n :local ListsDirName (\"fw-addr-lists.d/\" . [ /system identity get nam\ + e ] . \"/\");\r\ + \n :local ListsUrl (\$GlobalScriptsUrl . \$ListsDirName);\r\ + \n :local ListsIndex \"index.lst\";\r\ + \n :local AddrListsTimeOut 1d;\r\ + \n :local CheckCerts false;\r\ + \n\r\ + \n :global CertificateAvailable;\r\ + \n :global EitherOr;\r\ + \n :global FetchHugeFiles;\r\ + \n :global HumanReadableNum;\r\ + \n :global IfThenElse;\r\ + \n :global LogPrint;\r\ + \n :global NetMask4;\r\ + \n :global NetMask6;\r\ + \n :global ScriptLock;\r\ + \n \r\ + \n :local FindDelim do={\r\ + \n :local ValidChars \"0123456789.:/ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghi\ + jklmnopqrstuvwxyz-\";\r\ + \n :for I from=0 to=[ :len \$1 ] do={\r\ + \n :if ([ :typeof [ :find \$ValidChars [ :pick (\$1 . \" \") \$I ] ] \ + ] != \"num\") do={\r\ + \n :return \$I;\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :local GetBranch do={\r\ + \n :global EitherOr;\r\ + \n :return [ :pick [ :convert transform=md5 to=hex [ :pick \$1 0 [ \$Ei\ + therOr [ :find \$1 \"/\" ] [ :len \$1 ] ] ] ] 0 2 ];\r\ + \n }\r\ + \n \r\ + \n :if ([ \$ScriptLock \$ScriptName ] = false) do={\r\ + \n :set ExitOK true;\r\ + \n :error false;\r\ + \n }\r\ + \n\r\ + \n :local ListComment (\"managed by \" . \$ScriptName);\r\ + \n :local Url (\$ListsUrl . \$ListsIndex);\r\ + \n :local tmpIndex false;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :set tmpIndex [ :tolf [ \$FetchHugeFiles \$ScriptName \$Url \$Chec\ + kCerts ] ];\r\ + \n :if (\$tmpIndex = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list \ + index, \" . \$I . \". try from: \" . \$Url);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n :if (\$tmpIndex = false) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading address-list inde\ + x from: \" . \$Url);\r\ + \n :set tmpIndex \"\";\r\ + \n }\r\ + \n\r\ + \n :foreach List in=[ :deserialize \$tmpIndex delimiter=\"\\n\" from=dsv \ + options=dsv.plain ] do={\r\ + \n# :set List (\$List->0);\r\ + \n :local ListName;\r\ + \n :local Delim [ \$FindDelim \$List ];\r\ + \n :if (\$Delim = 0) do={ :set List \"\"; };\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$List);\r\ + \n :if ([ :len \$List ] = 0) do={\r\ + \n :log error (\$ScriptName . \" Failed parsing address-list index en\ + try: \" . \$List);\r\ + \n } else={\r\ + \n :set ListName [ :pick \$List 0 \$Delim ];\r\ + \n }\r\ + \n :log warning (\$ScriptName . \" Parsing address-list index entry: \"\ + \_. \$ListName . \" - Ok.\");\r\ + \n :local ListUrl (\$ListsUrl . \$ListName);\r\ + \n :local CntAdd 0;\r\ + \n :local CntRenew 0;\r\ + \n :local CntRemove 0;\r\ + \n :local IPv4Addresses ({});\r\ + \n :local IPv6Addresses ({});\r\ + \n :local Failure false;\r\ + \n :local Data false;\r\ + \n :local TimeOut \$AddrListTimeOut;\r\ + \n\r\ + \n :for I from=1 to=5 do={\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data [ :tolf [ \$FetchHugeFiles \$ScriptName \$ListUrl \$Ch\ + eckCerts ] ];\r\ + \n :if (\$Data = false && \$I < 5) do={\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\ + \" . \$ListName . \"', \" . \$I . \". try from: \" . \$ListUrl);\r\ + \n :delay ((\$I * \$I) . \"s\");\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :if (\$Data = false) do={\r\ + \n :set Data \"\";\r\ + \n :set Failure true;\r\ + \n :log warning (\$ScriptName . \" Failed downloading for list '\" . \ + \$ListName . \"' from: \" . \$ListUrl);\r\ + \n } else={\r\ + \n :log warning (\$ScriptName . \" Downloaded \" . [ \$HumanReadableN\ + um [ :len \$Data ] 1024 ] . \"B for list '\" . \$ListName . \"' from: \" .\ + \_\$ListUrl);\r\ + \n }\r\ + \n\r\ + \n :foreach Line in=[ :deserialize \$Data delimiter=\"\\n\" from=dsv op\ + tions=dsv.plain ] do={\r\ + \n :set Line (\$Line->0);\r\ + \n :local Address;\r\ + \n :if ([ :pick \$Line 0 1 ] = \"{\" && [ :pick \$Line ([ :len \$Line\ + \_] - 1) ] = \"}\") do={\r\ + \n :do {\r\ + \n :set Address [ :tostr ([ :deserialize from=json \$Line ]->\"ci\ + dr\") ];\r\ + \n } on-error={ }\r\ + \n } else={\r\ + \n :set Address ([ :pick \$Line 0 [ \$FindDelim \$Line ] ] . (\$L\ + ist->\"cidr\"));\r\ + \n }\r\ + \n :do {\r\ + \n :local Branch;\r\ + \n :if (\$Address ~ \"^[0-9]{1,3}\\\\.[0-9]{1,3}\\\\.[0-9]{1,3}\\\\\ + .[0-9]{1,3}(/[0-9]{1,2})\?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 32;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n :set Address [ :tostr (([ :toip \$Net ] & [ \$NetMask4 \$CID\ + R ]) . [ \$IfThenElse (\$CIDR < 32) (\"/\" . \$CIDR) ]) ];\r\ + \n }\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[0-9a-zA-Z]*:[0-9a-zA-Z:\\\\.]+(/[0-9]{1,3})\ + \?\\\$\") do={\r\ + \n :local Net \$Address;\r\ + \n :local CIDR 128;\r\ + \n :local Slash [ :find \$Address \"/\" ];\r\ + \n :if ([ :typeof \$Slash ] = \"num\") do={\r\ + \n :set Net [ :toip6 [ :pick \$Address 0 \$Slash ] ]\r\ + \n :set CIDR [ :pick \$Address (\$Slash + 1) [ :len \$Address ]\ + \_];\r\ + \n }\r\ + \n :set Address (([ :toip6 \$Net ] & [ \$NetMask6 \$CIDR ]) . \"/\ + \" . \$CIDR);\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n :if (\$Address ~ \"^[\\\\.a-zA-Z0-9-]+\\\\.[a-zA-Z]{2,}\\\$\") d\ + o={\r\ + \n :set Branch [ \$GetBranch \$Address ];\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address) \$TimeOut;\r\ + \n }\r\ + \n } on-error={ }\r\ + \n }\r\ + \n \r\ + \n\r\ + \n :foreach Entry in=[ /ip/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ip/firewall/address-list/get \$Entry address ];\ + \r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv4 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ip/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv4Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv4 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ip/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach Entry in=[ /ipv6/firewall/address-list/find where \\\r\ + \n list=\$ListName comment=\$ListComment ] do={\r\ + \n :local Address [ /ipv6/firewall/address-list/get \$Entry address ]\ + ;\r\ + \n :local Branch [ \$GetBranch \$Address ];\r\ + \n :local TimeOut (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :if ([ :typeof \$TimeOut ] = \"time\") do={\r\ + \n :log warning (\$ScriptName . \" Renewing IPv6 address \" . \$Add\ + ress . \\\r\ + \n \" in list '\" . \$ListName . \"' with \" . \$TimeOut . \".\ + \");\r\ + \n /ipv6/firewall/address-list/set \$Entry timeout=\$TimeOut;\r\ + \n :set (\$IPv6Addresses->\$Branch->\$Address);\r\ + \n :set CntRenew (\$CntRenew + 1);\r\ + \n } else={\r\ + \n :if (\$Failure = false) do={\r\ + \n :log warning (\$ScriptName . \" Removing IPv6 address \" . \$A\ + ddress . \\\r\ + \n \" from list '\" . \$ListName . \".\");\r\ + \n /ipv6/firewall/address-list/remove \$Entry;\r\ + \n :set CntRemove (\$CntRemove + 1);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv4Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv4 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ip/firewall/address-list/add list=\$ListName comment=\$ListCo\ + mment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv4 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :foreach BranchName,Branch in=\$IPv6Addresses do={\r\ + \n :log warning (\$ScriptName . \" Handling branch: \" . \$BranchName\ + );\r\ + \n :foreach Address,Timeout in=\$Branch do={\r\ + \n :log warning (\$ScriptName . \" Adding IPv6 address \" . \$Addre\ + ss . \\\r\ + \n \" to list '\" . \$ListName . \"' with \" . \$Timeout . \".\ + \");\r\ + \n :onerror Err {\r\ + \n /ipv6/firewall/address-list/add list=\$ListName comment=\$List\ + Comment \\\r\ + \n address=\$Address timeout=\$Timeout;\r\ + \n :set CntAdd (\$CntAdd + 1);\r\ + \n } do={\r\ + \n :log warning (\$ScriptName . \" Failed to add IPv6 address \" \ + . \$Address . \\\r\ + \n \" to list '\" . \$ListName . \"': \" . \$Err);\r\ + \n }\r\ + \n }\r\ + \n }\r\ + \n\r\ + \n :log info (\$ScriptName . \" list: \" . \$ListName . \\\r\ + \n \" (\" . [ \$HumanReadableNum (\$CntAdd + \$CntRenew) 1000 ] . \ + \")\" . \\\r\ + \n \" -- added: \" . [ \$HumanReadableNum \$CntAdd 1000 ] . \\\r\ + \n \" - renewed: \" . [ \$HumanReadableNum \$CntRenew 1000 ] . \\\r\ + \n \" - removed: \" . [ \$HumanReadableNum \$CntRemove 1000 ]);\r\ + \n }\r\ + \n} do={\r\ + \n :global ExitError; \$ExitError \$ExitOK [ :jobname ] \$Err;\r\ + \n}\r\ + \n" +add dont-require-permissions=no name=awg-env owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/envs/add list=awg-proxy-env key=AWG_LISTEN value=\":55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_REMOTE value=\"109.107.17\ + 7.79:55055\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H1 value=\"472437748-4724\ + 38637\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H2 value=\"707808381-7078\ + 09250\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H3 value=\"1609078552-160\ + 9079495\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_H4 value=\"1012329484-101\ + 2329741\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_I5 value=\"\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_SERVER_PUB value=\"o5IgCl\ + 1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-proxy-env key=AWG_CLIENT_PUB value=[/interf\ + ace/wireguard/get [find name=wg-awg-proxy] public-key]" +add dont-require-permissions=no name=awg-container-add owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="/\ + container/add file=sata1/tmp/awg-proxy-amd64.tar.gz interface=veth-awg-pro\ + xy envlist=awg-proxy-env \\\ + \n hostname=awg-proxy root-dir=sata1/awg-proxy logging=yes shm-size=4M \ + start-on-boot=yes\ + \n/container/start [find where tag~\"awg-proxy\"]" +add dont-require-permissions=yes name=awg2-webui owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source=":\ + put \"Download image, and save him to usb1/tmp\"\ + \n/tool/fetch url=\"https://ros.vados.ru/docker/awg2-webui-chr.tar\" dst-p\ + ath=sata1/tmp/awg2-webui.tar\ + \n\ + \n:put \"1. Create veth interface\"\ + \n/interface veth add name=veth-awg address=10.9.9.2/24 gateway=10.9.9.1\ + \n\ + \n:put \"2. Create bridge and add veth\"\ + \n/interface bridge add name=br-awg\ + \n/interface bridge port add bridge=br-awg interface=veth-awg\ + \n\ + \n:put \"3. Assign IP to bridge\"\ + \n/ip address add address=10.9.9.1/24 interface=br-awg\ + \n\ + \n:put \"4. NAT: masquerade container traffic\"\ + \n/ip firewall nat add chain=srcnat src-address=10.9.9.0/24 action=masquer\ + ade\ + \n\ + \n:put \"5. Port forward: UDP 443 \? container (AmneziaWG QUIC mimicry)\"\ + \n/ip firewall nat add chain=dstnat protocol=udp dst-port=443 action=dst-n\ + at to-addresses=10.9.9.2 to-ports=55055 in-interface=ether1\ + \n\ + \n:put \"6. Port forward: HTTPS 9443 \? container Web UI\"\ + \n/ip firewall nat add chain=dstnat protocol=tcp dst-port=9443 action=dst-\ + nat to-addresses=10.9.9.2 to-ports=443 in-interface=ether1\ + \n\ + \n:put \"Create environment list..\"\ + \n/container envs add list=awgui-env key=WEB_USER value=vados\ + \n/container envs add list=awgui-env key=WEB_PASS value=zoomzoomrulezz\ + \n/container envs add list=awgui-env key=SECRET_KEY value=RanDomSecret32Sy\ + mboLs2325272931q\ + \n/container envs add list=awgui-env key=AWG_ENDPOINT value=10.30.30.10:44\ + 3\ + \n\ + \n:put \"Load and start container...\"\ + \n/container add file=sata1/tmp/awg2-webui.tar interface=veth-awg hostname\ + =awg2-webui \\\ + \nroot-dir=sata1/awg2-webui layer-dir=sata1/docker/awg2-webu/ envlist=awgu\ + i-env start-on-boot=yes logging=yes shm-size=4M\ + \n\ + \n:put \"Start...\"\ + \n/container start 0\ + \n\ + \n:put \"Verify...\"\ + \n/container print\ + \n\ + \n:put \"Should show: status=running\"\ + \n\ + \n/log print where topics~\"container\"\ + \n\ + \n:put \"Should show: AWG started, nginx running\"\ + \n" +add dont-require-permissions=no name=awg-proxy-go-Client owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:put \"\"\ + \n:put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n /interface/bridge add name=docker port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=docker\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n" +add comment=awg2wg dont-require-permissions=no name=awg2wg-uninstall owner=\ + Manager policy=ftp,read,write,policy source="\r\ + \n :put \"Uninstalling AmneziaWG...\"\r\ + \n :log info \"Uninstalling AmneziaWG...\"\r\ + \n /ip/route/remove [find where comment=awg2wg-tunnel]\r\ + \n :do { /tool/netwatch/remove [find where comment=Monitor-AWG2WG] } on-e\ + rror={}\r\ + \n :do { /ip/route/remove [find where comment=Monitor-AWG2WG] } on-error=\ + {}\r\ + \n /container/stop [find where interface=veth-awg2wg]\r\ + \n :delay 7s\r\ + \n /interface/list/member/remove [find where interface=wg-awg2wg]\r\ + \n /container/remove [find where interface=veth-awg2wg]\r\ + \n /container/envs/remove [find where list=\"awg2wg-env\"]\r\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg2wg\"]\r\ + \n /ip/address/remove [find where address=\"172.20.0.1/30\"]\r\ + \n /interface/veth/remove [find where name=\"veth-awg2wg\"]\r\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg2wg\"]\ + \r\ + \n /ip/address/remove [find where interface=\"wg-awg2wg\"]\r\ + \n /interface/wireguard/remove [find where name=\"wg-awg2wg\"]\r\ + \n :do { /file/remove [find where name~\"awg2wg.+tar\"] } on-error={}\r\ + \n :do { /file/remove [find where name=\"disk1/awg2wg\"] } on-error={}\r\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\r\ + \n /system/script/remove [find where name=awg2wg-uninstall]\r\ + \n" +add dont-require-permissions=no name=amneziawg-mikrotik-c owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:global GlobalScriptsUrl \"https://ros.vados.ru/\";\ + \n:global imagesPath \"amneziawg/img/\";\ + \n:local Image \"awg-proxy-amd64-7.20-Docker.tar.gz\";\ + \n\ + \n:global DstDir \"sata1/dst/\";\ + \n\ + \n:local Url=(\$GlobalScriptsUrl . \$ImagesPath . \$Image);\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n" +add comment=Generator dont-require-permissions=no name=awg-VDSina owner=vados \ + policy=ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + source="# ============================================================\ + \n# AWG Proxy -- MikroTik RouterOS (protocol: v2)\ + \n# 2026-06-30 22:08:12\ + \n# ============================================================\ + \n\ + \n# 0. Check prerequisites\ + \n:if ([:len [/system/package/find where name=\"container\" disabled=no]] \ + = 0) do={\ + \n :put \"Container package is not installed or disabled. Install it and \ + reboot.\"\ + \n :error \"Usage: /system/device-mode/update container=yes\"\ + \n}\ + \n\ + \n# Auto-detect storage\ + \n:global awgDisk \"disk1\"\ + \n{\ + \n :local usbFree 0\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] free] \ + } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n :put (\"Using USB: usb1 (\" . (\$usbFree / 1048576) . \" MB free)\")\ + \n } else={\ + \n :if ([:len [/disk/find where slot=\"usb1\"]] > 0) do={\ + \n :put \"USB found but not mounted. Format as ext4\?\"\ + \n :put \"Press 'y' to confirm:\"\ + \n :local key [/terminal/inkey timeout=30]\ + \n :if (\$key = 121 || \$key = 89) do={\ + \n /disk/format-drive usb1 file-system=ext4 label=usb1\ + \n :delay 3s\ + \n :do { :set usbFree [/disk/get [find where mount-point=\"usb1\"] \ + free] } on-error={}\ + \n :if (\$usbFree > 0) do={\ + \n :set awgDisk \"usb1\"\ + \n } else={ :put \"Format failed, using disk1\" }\ + \n } else={ :put \"Using disk1\" }\ + \n } else={ :put \"No USB, using disk1\" }\ + \n }\ + \n}\ + \n\ + \n# 1. Uninstall script\ + \n/system/script/add name=awg-VDSina-uninstall comment=awg-VDSina source={\ + \n :put \"Uninstallimg AmneziaWG...\"\ + \n :log info \"Uninstallimg AmneziaWG...\"\ + \n :local prevDNS \"\"\ + \n :do { :set prevDNS [/container/envs/get [find where list=\"awg-VDSina-\ + env\" key=\"AWG_PREV_DNS\"] value] } on-error={}\ + \n :local storageDisk \"disk1\"\ + \n :do { :set storageDisk [/container/envs/get [find where list=\"awg-VDS\ + ina-env\" key=\"AWG_STORAGE\"] value] } on-error={}\ + \n :local prevTmpdir \"\"\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { :set prevTmpdir [/container/envs/get [find where list=\"awg-VD\ + Sina-env\" key=\"AWG_PREV_TMPDIR\"] value] } on-error={}\ + \n }\ + \n /ip/route/remove [find where comment=awg-VDSina-tunnel]\ + \n /ip/route/remove [find where comment=awg-VDSina-dns]\ + \n /container/stop [find where interface=veth-awg-VDSina]\ + \n :delay 7s\ + \n /container/remove [find where interface=veth-awg-VDSina]\ + \n /container/envs/remove [find where list=\"awg-VDSina-env\"]\ + \n :if (\$prevDNS != \"\") do={\ + \n :do { /ip/dns/set servers=\"\$prevDNS\" } on-error={}\ + \n :put (\"DNS restored to: \$prevDNS\")\ + \n }\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n /container/config set tmpdir=\$prevTmpdir\ + \n :put (\"Container tmpdir restored to: \$prevTmpdir\")\ + \n }\ + \n /ip/firewall/nat/remove [find where out-interface=\"wg-awg-VDSina\"]\ + \n /ip/firewall/nat/remove [find where src-address=\"172.19.75.208/30\"]\ + \n /ip/address/remove [find where address=\"172.19.75.209/30\"]\ + \n /interface/veth/remove [find where name=\"veth-awg-VDSina\"]\ + \n /interface/wireguard/peers/remove [find where interface=\"wg-awg-VDSin\ + a\"]\ + \n /ip/address/remove [find where interface=\"wg-awg-VDSina\"]\ + \n /interface/wireguard/remove [find where name=\"wg-awg-VDSina\"]\ + \n :do { /file/remove [find where name~\"awg-VDSina.+tar\"] } on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/awg-VDSina\")]\ + \_} on-error={}\ + \n :do { /file/remove [find where name=(\$storageDisk . \"/ru_ranges_time\ + out.rsc\")] } on-error={}\ + \n :if (\$storageDisk != \"disk1\") do={\ + \n :do { /file/remove [find where name~(\$storageDisk . \"/pull\")] } o\ + n-error={}\ + \n }\ + \n :put \"Uninstall AmneziaWG Proxy complete!\"\ + \n :log info \"Uninstall AmneziaWG Proxy complete!\"\ + \n /system/script/remove [find where name=awg-VDSina-uninstall]\ + \n}\ + \n\ + \n# 2. Network infrastructure\ + \n/interface/veth/add name=veth-awg-VDSina address=172.19.75.210/30 gatewa\ + y=172.19.75.209\ + \n/ip/address/add address=172.19.75.209/30 interface=veth-awg-VDSina\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.19.7\ + 5.208/30\ + \n\ + \n# 3. WireGuard interface (MikroTik derives the public key automatically)\ + \n/interface/wireguard/add name=wg-awg-VDSina private-key=\"4CHyNhbOs923/E\ + yNDguMXJXdghL2I3UyzXPwrANVgHs=\" listen-port=14710 disabled=yes\ + \n/interface/wireguard/peers/add interface=wg-awg-VDSina public-key=\"o5Ig\ + Cl1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\" preshared-key=\"RNvF7T1cg5tyXcY\ + QnD9N5BEp6e20vVJQTv0GMjyvHU0=\" endpoint-address=172.19.75.210 endpoint-po\ + rt=51820 allowed-address=0.0.0.0/0 persistent-keepalive=10\ + \n/ip/address/add address=10.9.0.3/32 interface=wg-awg-VDSina\ + \n\ + \n# 4. Container environment variables\ + \n/container/envs/add list=awg-VDSina-env key=AWG_LISTEN value=\":51820\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_REMOTE value=\"109.107.1\ + 77.79:55055\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JC value=\"9\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMIN value=\"105\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_JMAX value=\"920\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S1 value=\"90\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S2 value=\"136\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H1 value=\"472437748-472\ + 438637\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H2 value=\"707808381-707\ + 809250\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H3 value=\"1609078552-16\ + 09079495\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_H4 value=\"1012329484-10\ + 12329741\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_SERVER_PUB value=\"o5IgC\ + l1/G6xbl6j7sPaP4ZNYREeYOmqZjKbm+v0b9jU=\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_CLIENT_PUB value=[/inter\ + face/wireguard/get [find name=wg-awg-VDSina] public-key]\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S3 value=\"45\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_S4 value=\"27\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I1 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I2 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I3 value=\"\ + \"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I4 value=\"\"\ + \n/container/envs/add list=awg-VDSina-env key=AWG_I5 value=\"\"\ + \n\ + \n# 5. DNS\ + \n{ :local rawDNS [/ip/dns/get servers]; :local prevDNS \"\"; :foreach s i\ + n=[:toarray \$rawDNS] do={ :if ([:len \$prevDNS] > 0) do={ :set prevDNS \"\ + \$prevDNS,\$s\" } else={ :set prevDNS \$s } }; /container/envs/add list=aw\ + g-VDSina-env key=AWG_PREV_DNS value=\"\$prevDNS\" }\ + \n# NOTE: replaces existing DNS configuration\ + \n/ip/dns/set servers=109.107.177.79\ + \n/ip/route/add dst-address=109.107.177.79/32 gateway=wg-awg-VDSina distan\ + ce=1 comment=awg-VDSina-dns\ + \n\ + \n# 6. Download, create and start container\ + \n/container/envs/add list=awg-VDSina-env key=AWG_STORAGE value=\$awgDisk\ + \n:if (\$awgDisk != \"disk1\") do={\ + \n :local tmpPath (\$awgDisk . \"/pull\")\ + \n /container/envs/add list=awg-VDSina-env key=AWG_PREV_TMPDIR value=[/co\ + ntainer/config/get tmpdir]\ + \n /container/config set tmpdir=\$tmpPath memory-high=200M\ + \n}\ + \n{\ + \n :local arch [/system/resource/get architecture-name]\ + \n :local ver [/system/resource/get version]\ + \n :local dotPos [:find \$ver \".\"]\ + \n :local rest [:pick \$ver (\$dotPos + 1) [:len \$ver]]\ + \n :local endPos [:find \$rest \".\"]\ + \n :if ([:typeof \$endPos] = \"nil\") do={ :set endPos [:find \$rest \" \ + \"] }\ + \n :local minor [:tonum [:pick \$rest 0 \$endPos]]\ + \n :local suffix \"\"\ + \n :if (\$minor <= 20) do={ :set suffix \"-7.20-Docker\" }\ + \n :local file \"\"\ + \n :if (\$arch = \"arm64\") do={ :set file (\"awg-proxy-arm64\" . \$suffi\ + x . \".tar.gz\") }\ + \n :if (\$arch = \"arm\") do={ :set file (\"awg-proxy-arm\" . \$suffix . \ + \".tar.gz\") }\ + \n :if (\$arch ~ \"x86\") do={ :set file (\"awg-proxy-amd64\" . \$suffix \ + . \".tar.gz\") }\ + \n :if (\$file = \"\") do={ :error \"Unsupported architecture: \$arch\" }\ + \n :local url \"https://github.com/timbrs/amneziawg-mikrotik-c/releases/l\ + atest/download/\$file\"\ + \n :local filePath \$file\ + \n :if (\$awgDisk != \"disk1\") do={\ + \n :set filePath (\$awgDisk . \"/pull/\" . \$file)\ + \n :do { /file/make-directory (\$awgDisk . \"/pull\") } on-error={}\ + \n }\ + \n :if ([:len [/file/find where name=\$filePath]] = 0) do={\ + \n :local freeStorage 0\ + \n :if (\$awgDisk = \"disk1\") do={\ + \n :set freeStorage [/system/resource/get free-hdd-space]\ + \n } else={\ + \n :do { :set freeStorage [/disk/get [find where mount-point=\$awgDis\ + k] free] } on-error={}\ + \n }\ + \n :if (\$freeStorage < 262144) do={\ + \n :put (\"WARNING: Low disk space (\" . (\$freeStorage / 1024) . \"K\ + B free). Need at least 256KB.\")\ + \n :put \"See: https://github.com/timbrs/amneziawg-mikrotik-c#insuffi\ + cient-disk-space\"\ + \n :error \"Insufficient disk space\"\ + \n }\ + \n :put (\"Fetching: \$url -> \" . \$filePath)\ + \n /tool/fetch url=\$url dst-path=\$filePath http-max-redirect-count=10\ + \n } else={\ + \n :put (\"File already exists: \" . \$filePath)\ + \n }\ + \n :local rootDir (\$awgDisk . \"/awg-VDSina\")\ + \n /container/add file=\$filePath interface=veth-awg-VDSina envlist=awg-V\ + DSina-env hostname=awg-VDSina root-dir=\$rootDir logging=no start-on-boot=\ + yes comment=awg-VDSina\ + \n :if (\$minor > 20) do={ [:parse \"/container/set [find where interface\ + =veth-awg-VDSina] shm-size=4M\"] }\ + \n /file/remove \$filePath\ + \n :local freeMem [/system/resource/get free-memory]\ + \n :if (\$freeMem < 16777216) do={\ + \n :put (\"WARNING: Low memory (\" . (\$freeMem / 1048576) . \"MB free)\ + . 16MB+ recommended.\")\ + \n }\ + \n /container/start [find where interface=veth-awg-VDSina]\ + \n :do { /file/remove [find where name=\"console-dump.txt\"] } on-error={\ + }\ + \n :put \"Waiting for container to start...\"\ + \n :delay 5s\ + \n /interface/wireguard/enable wg-awg-VDSina\ + \n :put \"WireGuard interface enabled\"\ + \n :put \"Installation complete!\"\ + \n :put \"Ping test: 109.107.177.79 via AmneziaWG tunnel. repeat 10 times\ + \"\ + \n /ping 109.107.177.79 count=10 interval=1\ + \n :put \"\"\ + \n :put \"======= NEXT STEPS =======\"\ + \n :put \"\"\ + \n :put \"Route specific traffic through the AmneziaWG tunnel:\"\ + \n :put \"\"\ + \n :put \" Route a single host:\"\ + \n :put \" /ip/route/add dst-address=8.8.8.8/32 gateway=wg-awg-VDSina \ + comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Route a subnet:\"\ + \n :put \" /ip/route/add dst-address=100.0.0.0/8 gateway=wg-awg-VDSina\ + \_comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Remove a route:\"\ + \n :put \" /ip/route/remove [find where comment=awg-VDSina-tunnel dst-\ + address=8.8.8.8/32]\"\ + \n :put \"\"\ + \n :put \" Show all tunnel routes:\"\ + \n :put \" /ip/route/print where comment=awg-VDSina-tunnel\"\ + \n :put \"\"\ + \n :put \" Uninstall:\"\ + \n :put \" /system/script/run awg-VDSina-uninstall\"\ + \n :put \"=========================\"\ + \n}\ + \n\ + \n# 7. Verification\ + \n# /container/print\ + \n# /interface/wireguard/print\ + \n# /interface/wireguard/peers/print\ + \n" +add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ + "wg2awg client" owner=vados policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ + \n:put \"[::::::::::::::: START :::::::::::::::]\"\ + \n:local Name wg2awg;\ + \n:local Bridge (\"Bridge\" . \$Name);\ + \n#:put \"\"\ + \n#put \"Add ram disk tmpfs\"\ + \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ + \n\ + \n:put \"veth and network:\"\ + \n/interface/veth/add name=veth-awg address=172.18.0.2/30 gateway=172.18.0\ + .1\ + \n/ip/address/add address=172.18.0.1/30 interface=veth-awg\ + \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.18.0\ + .0/30\ + \n /interface/bridge add name=Bridge-\$name port-cost-mode=short\ + \n /ip/address/add address=192.168.100.1/24 interface=Bridge-\$name\ + \n\ + \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ + =awg-proxy-go-client\ + \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ + \n\ + \n:put \"add config-client.json\"\ + \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ + \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ + st=/app/config.json list=awgcfg-client\ + \n\ + \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ + d64.tar.gz\"\ + \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ + 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ + \n\ + \n:put \"Add container\"\ + \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ + =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ + -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ + xy-go-client\" mountlists=awgcfg-client\ + \n\ + \n:put \"Start container\"\ + \n#/container/start awg-proxy-go-client\ + \n\ + \n:put \"Add user:\"\ + \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ + peskar55\ + \n\ + \n:put \"Add forwarding port web-ui\"\ + \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ + face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ + \n\ + \n:put \"Add native WireGuard-server wg-awg, add address.\"\ + \n /interface/wireguard/add name=wg-awg-client\ + \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ + \n\ + \n:put \"\"\ + \n:put \"[::::::::::::::: END :::::::::::::::]\"\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \n\ + \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" +add name=manager policy="local,ssh,ftp,read,write,policy,api,rest-api,!telnet,\ + !reboot,!test,!winbox,!password,!web,!sniff,!sensitive,!romon" +/app +set cinny firewall-redirects=8094:80:tcp:web +set goaway container-command-lines=goaway:none:docker.io/pommee/goaway:latest +set home-assistant container-command-lines=\ + home-assistant:none:lscr.io/linuxserver/homeassistant +set n8n firewall-redirects=5678:5678:tcp:web +set nextcloud container-command-lines="db:none:docker.io/postgres:17,redis:non\ + e:docker.io/valkey/valkey:/bin/sh -c 'valkey-server --port 6379 --appendon\ + ly yes --requirepass \$VALKEY_PASSWORD',server:none:docker.io/nextcloud:ap\ + ache" +set pihole environment="pihole:FTLCONF_dns_listeningMode=all,pihole:FTLCONF_we\ + bserver_api_password=password" +set redlib firewall-redirects=8087:8080:tcp:web +set solr container-command-lines=solr:none:docker.io/solr:latest +set uptime-kuma container-command-lines=\ + uptime-kuma:none:docker.io/louislam/uptime-kuma:1 +/app settings +set auto-update=yes disk=*7 lan-bridge=*3 registry-mirrors="https://reg.vados.\ + ru:5000/:https://reg.vados.ru/,https://registry-1.docker.io" +/caps-man aaa +set called-format=ssid +/caps-man access-list +add action=reject allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-120..-80 ssid-regexp="" +add action=accept allow-signal-out-of-range=10s disabled=no interface=all \ + signal-range=-79..120 ssid-regexp="" +/caps-man configuration +add channel.band=2ghz-b/g/n country=no_country_set datapath=xs-DataPath mode=\ + ap name=xs-2G rx-chains=0,1,2,3 security=xs-Sec ssid=vados tx-chains=\ + 0,1,2,3 +add channel.band=5ghz-a/n/ac country=no_country_set datapath=xs-DataPath \ + installation=any mode=ap name=xs-5G rx-chains=0,1,2,3 security=xs-Sec \ + ssid=vados5g tx-chains=0,1,2,3 +add channel.band=2ghz-b/g/n .control-channel-width=20mhz .extension-channel=\ + XX .frequency=2422,2442,2462 .reselect-interval=20m country=belarus \ + datapath=DataPath datapath.client-to-client-forwarding=yes \ + .local-forwarding=yes hw-protection-mode=rts-cts installation=any name=2G \ + rx-chains=0,1,2,3 security=WPA2 ssid=vados tx-chains=0,1,2,3 +add channel.band=5ghz-a/n/ac .control-channel-width=20mhz .extension-channel=\ + XXXX .frequency=5180,5200,5220,5240,5260 .reselect-interval=20m \ + .skip-dfs-channels=yes country=belarus datapath=DataPath \ + datapath.client-to-client-forwarding=yes .local-forwarding=yes \ + hw-protection-mode=rts-cts installation=any name=5G security=WPA2 ssid=\ + vados5g +/caps-man datapath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + xs-DataPath +add bridge=*3 client-to-client-forwarding=yes local-forwarding=yes name=\ + DataPath +/caps-man manager +set ca-certificate=CAPsMAN-CA-51B25E507D07 certificate=CAPsMAN-51B25E507D07 \ + enabled=yes require-peer-certificate=yes +/caps-man manager interface +set [ find default=yes ] forbid=yes +add disabled=no interface=BridgeLAN +/caps-man provisioning +add action=create-dynamic-enabled hw-supported-modes=g,gn,b \ + master-configuration=2G name-format=prefix-identity name-prefix=2G +add action=create-dynamic-enabled hw-supported-modes=an,ac,a \ + master-configuration=5G name-format=prefix-identity name-prefix=5G +add action=create-dynamic-enabled disabled=yes hw-supported-modes=ac,an,a \ + master-configuration=2G name-format=prefix-identity name-prefix=5ghz-ac +add action=create-dynamic-enabled disabled=yes hw-supported-modes=an \ + master-configuration=5G name-format=prefix-identity name-prefix=5ghz-an +/certificate settings +set crl-store=system +/container config +set layer-dir=/sata1/docker/layers memory-high=200.0MiB memory-max=4096.0MiB \ + registry-url=https://registry-1.docker.io tmpdir=/sata1/tmp username=\ + amsternl +/ip smb +set enabled=no +/interface bridge port +add bridge=BridgeLAN interface=ether1 +add bridge=BridgeLAN interface=2G-MikroGig-1 +add bridge=BridgeLAN interface=2G-MikroMaster-1 +add bridge=BridgeLAN interface=5G-MikroGig-1 +add bridge=BridgeLAN interface=5G-MikroMaster-1 +/ipv6 settings +set allow-fast-path=no disable-ipv6=yes disable-link-local-address=yes +/interface detect-internet +set detect-interface-list=all internet-interface-list=Internet \ + lan-interface-list=LAN wan-interface-list=WAN +/interface list member +add interface=ether1 list=WAN +/interface wifi cap +set discovery-interfaces=*DF +/interface wifi capsman +set package-path=/tmpfs/capsman-packages upgrade-policy=none +/ip address +add address=10.30.30.10/24 interface=BridgeLAN network=10.30.30.0 +/ip dhcp-client +add disabled=yes interface=ether1 name=client1 +/ip dhcp-server +add add-arp=yes address-pool=dhcp always-broadcast=yes bootp-support=none \ + dhcp-option-set=domain-search-set disabled=yes interface=*3 lease-script=\ + lease-script lease-time=10m name=nucDHCP server-address=10.30.30.10 +/ip dns +set allow-remote-requests=yes doh-max-concurrent-queries=100 \ + doh-max-server-connections=20 servers=10.30.30.33,109.107.177.79 \ + verify-doh-cert=yes +/ip firewall service-port +set ftp disabled=yes +set tftp disabled=yes +set sip disabled=yes +set pptp disabled=yes +set rtsp disabled=no +/ip route +add check-gateway=none disabled=no distance=2 dst-address=0.0.0.0/0 gateway=\ + BridgeLAN routing-table=main scope=30 target-scope=10 +/ip service +set ftp disabled=yes +set reverse-proxy disabled=yes +set ssh port=2222 +set www-ssl port=4443 +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-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=*3 type=external +add interface=*B5 type=internal +/ipv6 firewall address-list +add address=2400:cb00::/32 list=CLOUDFLARE-IP-Address +add address=2606:4700::/32 list=CLOUDFLARE-IP-Address +add address=2803:f800::/32 list=CLOUDFLARE-IP-Address +add address=2405:b500::/32 list=CLOUDFLARE-IP-Address +add address=2405:8100::/32 list=CLOUDFLARE-IP-Address +add address=2a06:98c0::/29 list=CLOUDFLARE-IP-Address +add address=2c0f:f248::/32 list=CLOUDFLARE-IP-Address +/special-login +add port=serial0 +/system clock +set time-zone-autodetect=no time-zone-name=Europe/Moscow +/system gps +set set-system-time=yes +/system identity +set name=MikroNUC +/system note +set show-at-login=no +/system ntp client +set enabled=yes +/system ntp server +set enabled=yes manycast=yes multicast=yes +/system ntp client servers +add address=10.30.30.81 +add address=0.ru.pool.ntp.org +add address=1.ru.pool.ntp.org +add address=2.ru.pool.ntp.org +add address=3.ru.pool.ntp.org +/system scheduler +add comment="Every Day Set Global Environment Variables" disabled=yes name=\ + AM-SetGlobalEnv on-event=\ + "/system/script { run AM-GlobalConfig; run AM-GlobalFunc; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Update Scripts" disabled=yes interval=1d \ + name=AM-ScriptInstallUpdate on-event=\ + ":global ScriptInstallUpdate; \$ScriptInstallUpdate;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Every Day Check For Fake Bridges" disabled=yes interval=1d name=\ + mod/AM-IPbridge on-event="/system/script/run mod/AM-IPbridge;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment="Telegram Chat Bot" disabled=yes interval=30s name=mod/AM-TG-Bot \ + on-event="/system/script/run mod/AM-TG-Bot;" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-time=startup +add comment=\ + "Every 7 Day Check For Update and Backup send notification to EMail." \ + disabled=yes interval=1w name=AM-Backup-UpdateEmail on-event=\ + "/system/script { run AM-Backup-UpdateEmail; }" policy=\ + ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon \ + start-date=2026-06-25 start-time=03:41:27 +/tool e-mail +set certificate-verification=no from=MikroNUC port=587 \ + server=smtp.mail.me.com tls=starttls user=vadossio@icloud.com +/user aaa +set accounting=no diff --git a/MikroNUC/history/20260703/20260703004001-Diff-Config.rsc b/MikroNUC/history/20260703/20260703004001-Diff-Config.rsc new file mode 100644 index 0000000..61eac5c --- /dev/null +++ b/MikroNUC/history/20260703/20260703004001-Diff-Config.rsc @@ -0,0 +1,80 @@ +5047,5125d5046 +< \n" +< add comment="wg2awg GH repo in client mode" dont-require-permissions=no name=\ +< "wg2awg client" owner=vados policy=\ +< ftp,reboot,read,write,policy,test,password,sniff,sensitive,romon source="\ +< \n:put \"[::::::::::::::: START :::::::::::::::]\"\ +< \n:local Name wg2awg;\ +< \n:local Bridge (\"Bridge\" . \$Name);\ +< \n#:put \"\"\ +< \n#put \"Add ram disk tmpfs\"\ +< \n#/disk add type=tmpfs tmpfs-max-size=100M slot=ram\ +< \n\ +< \n:put \"veth and network:\"\ +< \n/interface/veth/add name=veth-awg address=172.18.0.2/30 gateway=172.18.0\ +< .1\ +< \n/ip/address/add address=172.18.0.1/30 interface=veth-awg\ +< \n/ip/firewall/nat/add chain=srcnat action=masquerade src-address=172.18.0\ +< .0/30\ +< \n /interface/bridge add name=Bridge-\$name port-cost-mode=short\ +< \n /ip/address/add address=192.168.100.1/24 interface=Bridge-\$name\ +< \n\ +< \n /interface/veth add address=192.168.100.3/24 gateway=192.168.100.1 name\ +< =awg-proxy-go-client\ +< \n /interface/bridge/port add bridge=docker interface=awg-proxy-go-client\ +< \n\ +< \n:put \"add config-client.json\"\ +< \n/file/add name=sata1/mounts/awg-proxy-go/config-client.json\ +< \n/container/mounts/add src=sata1/mounts/awg-proxy-go/config-client.json d\ +< st=/app/config.json list=awgcfg-client\ +< \n\ +< \n#:put \"Download image, and save him to sata1/tmp/awgproxy-0.0.1-0034-am\ +< d64.tar.gz\"\ +< \n#/tool/fetch url=\"https://ros.vados.ru/amneziawg/img/awgproxy-0.0.1-003\ +< 4-amd64.tar.gz\" dst-#path=\"tmp/awgproxy-0.0.1-0034-amd64.tar.gz\"\ +< \n\ +< \n:put \"Add container\"\ +< \n/container/add file=\"usb1/dst/awgproxy-0.0.1-0036-amd64.tar\" interface\ +< =awg-proxy-go-client hostname=awg-proxy-go-client root-dir=sata1/awg-proxy\ +< -client logging=yes start-on-boot=yes workdir=sata1/docker/ name=\"awg-pro\ +< xy-go-client\" mountlists=awgcfg-client\ +< \n\ +< \n:put \"Start container\"\ +< \n#/container/start awg-proxy-go-client\ +< \n\ +< \n:put \"Add user:\"\ +< \n /user/add address=192.168.100.2 group=write name=awg-proxy-go password=\ +< peskar55\ +< \n\ +< \n:put \"Add forwarding port web-ui\"\ +< \n /ip firewall nat add action=dst-nat chain=dstnat dst-port=8089 in-inter\ +< face=ether1 protocol=tcp to-addresses=192.168.100.3 to-port=8088\ +< \n\ +< \n:put \"Add native WireGuard-server wg-awg, add address.\"\ +< \n /interface/wireguard/add name=wg-awg-client\ +< \n /ip/address/add address=172.17.0.1/24 interface=wg-awg-client\ +< \n\ +< \n:put \"\"\ +< \n:put \"[::::::::::::::: END :::::::::::::::]\"\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ +< \n\ -- 2.52.0