OK, here’s an updater:
<?php
use PEAR2\Net\RouterOS as R;
require_once 'PEAR2/Autoload.php';
$taskNamePrefix = 'ROSUpdater';
$addressListPrefix = '[ROSUpdater] ';
//Deal with the command line arguments.
if (!isset($argv)) {
die('This script must be executed from the command line.');
}
array_shift($argv);
$domain = trim((string) array_shift($argv));
$lists = $argv;
//Verify required arguments.
if ('' === $domain) {
die('You must specify a domain as a first argument.');
}
$client = new R\Client('ros.example.com', 'apifull', 'apifull');
//Purge old entries for this domain (for all lists!!!).
$request = new R\Request('/ip firewall address-list print .proplist=.id');
$idList = '';
foreach ($client->sendSync($request->setQuery(R\Query::where('comment', $addressListPrefix . $domain))) as $response) {
$idList .= $response->getArgument('.id') . ',';
}
$idList = rtrim($idList, ',');
if (!empty($idList)) {
$request = new R\Request('/ip firewall address-list remove');
$client->sendSync($request->setArgument('numbers', $idList));
}
if (empty($lists)) {
//
// Windows specific.
//
exec('schtasks /Delete /F /TN ' . escapeshellarg($taskNamePrefix . '\\' . $domain));
exec('schtasks /Query /FO TABLE /TN ' . escapeshellarg($taskNamePrefix . '\\\\'), $output);
if ('' === trim(implode('', $output))) {
exec('schtasks /Delete /F /TN ' . escapeshellarg($taskNamePrefix));
}
die('Domain purged. Tasks cleared. Nothing to add.');
}
//Add all IPs to each list.
$request = new R\Request('/ip firewall address-list add');
$request->setArgument('comment', $addressListPrefix . $domain);
foreach (gethostbynamel($domain) as $ip) {
$request->setArgument('address', $ip);
foreach ($lists as $list) {
$client->sendSync($request->setArgument('list', $list));
}
}
//Get reccomended refresh interval from DNS server.
$refreshAfter = dns_get_record($domain, DNS_SOA);
$refreshAfter = $refreshAfter[0]['refresh'];
//Calculate next refresh.
$updateTime = new DateTime("now +{$refreshAfter} seconds");
$updateTime = $updateTime->format('c');
//Generate the arguments string for the task.
$arguments = '-f ' . escapeshellarg(__FILE__) . ' -- ' . escapeshellarg($domain) . ' ';
foreach ($lists as $list) {
$arguments .= escapeshellarg($list) . ' ';
}
$arguments = rtrim($arguments, ' ');
//
// Everything below is applicable only to Windows, for the purpose of creating a scheduled task.
// Adjust accordingly for Cron.
//
//Save the XML file temporarily, so that we can create a task from it.
$resultPath = __DIR__ . DIRECTORY_SEPARATOR . $domain . '.xml';
file_put_contents($resultPath,
<<<HEREDOC
<?xml version="1.0" encoding="UTF-16"?>
<Task version="1.2" xmlns="http://schemas.microsoft.com/windows/2004/02/mit/task">
<Triggers>
<TimeTrigger>
<StartBoundary>{$updateTime}</StartBoundary>
<EndBoundary>{$updateTime}</EndBoundary>
<Enabled>true</Enabled>
</TimeTrigger>
</Triggers>
<Principals>
<Principal id="Author">
<RunLevel>HighestAvailable</RunLevel>
</Principal>
</Principals>
<Settings>
<MultipleInstancesPolicy>IgnoreNew</MultipleInstancesPolicy>
<DisallowStartIfOnBatteries>true</DisallowStartIfOnBatteries>
<StopIfGoingOnBatteries>true</StopIfGoingOnBatteries>
<AllowHardTerminate>true</AllowHardTerminate>
<StartWhenAvailable>true</StartWhenAvailable>
<RunOnlyIfNetworkAvailable>true</RunOnlyIfNetworkAvailable>
<IdleSettings>
<StopOnIdleEnd>true</StopOnIdleEnd>
<RestartOnIdle>false</RestartOnIdle>
</IdleSettings>
<AllowStartOnDemand>true</AllowStartOnDemand>
<Enabled>true</Enabled>
<Hidden>false</Hidden>
<RunOnlyIfIdle>false</RunOnlyIfIdle>
<WakeToRun>true</WakeToRun>
<ExecutionTimeLimit>PT1H</ExecutionTimeLimit>
<DeleteExpiredTaskAfter>PT0S</DeleteExpiredTaskAfter>
<Priority>7</Priority>
</Settings>
<Actions Context="Author">
<Exec>
<Command>"D:\PHP\php.exe"</Command>
<Arguments>{$arguments}</Arguments>
</Exec>
</Actions>
</Task>
HEREDOC
);
exec('schtasks /Create /RU SYSTEM /RP "" /F /XML ' . escapeshellarg($resultPath) . ' /TN ' . escapeshellarg($taskNamePrefix . '\\' . $domain));
unlink($resultPath);
//If you can read that, without anything else on screen, chances are there are no errors.
echo 'OK';
After adjusting your router details and PHP location in the XML, the way you use it is from the command line (running as Administrator!!!), as
php -f file -- domain [list [...]]
e.g.
D:\PHP\php.exe -f updater.php -- google.com profile1-filters profile2-filters
will add “google.com” to the address lists “profile1-filters” and “profile2-filters”.
You can clear entries for a domain (and its scheduled task) by running without specifying lists, e.g.
D:\PHP\php.exe -f updater.php -- google.com
to purge google.com entries.
Previously, at the router, you’d have to set user profiles like
/ip hotspot user profile add name=profile1 address-list=profile1
and a corresponding filter rule (one per profile) like
/ip firewall filter add protocol=tcp dst-port=80 src-address-list=profile1 dst-address-list=profile1-filters chain=forward action=drop
or if you want to show an error page, you could use a NAT rule instead of a filter rule, like:
/ip firewall nat add protocol=tcp dst-port=80 src-address-list=profile1 dst-address-list=profile1-filters chain=dstnat action=dst-nat to-address=192.168.0.254 to-ports=80
(replacing 192.168.0.254 with the IP of your web server, and adjusting the port if necessary)
As designed, it currently works only with Windows’ scheduler, but you can easily adjust it for UNIX’s cron instead.