Skip to content

Instantly share code, notes, and snippets.

@uablacklist
Last active July 9, 2024 22:11
Show Gist options
  • Save uablacklist/b0e73deb75f2a1c43f035c59e5401e8c to your computer and use it in GitHub Desktop.
Save uablacklist/b0e73deb75f2a1c43f035c59e5401e8c to your computer and use it in GitHub Desktop.
Добавляет IP подсети, заблокированные украинским правительством, из https://uablacklist.net в список адресов вашего Mikrotik
:local apiPrefix "https://uablacklist.net/subnets_mikrotik_"
:local tempFile "uablacklist.txt"
:local listName "uablacklist"
/log info "removing existing '$listName'..."
:put "removing existing '$listName'..."
/ip firewall address-list remove [/ip firewall address-list find list=$listName]
# mikrotik can't load >4KB file inside a variable, so backend contains file pieces which we download and parse one by one
:local i 0
:local isEnd false
:do {
:local apiPath "$apiPrefix$i.txt"
/log info "fetching UA blacklist registry piece ($apiPath)..."
:put "fetching UA blacklist registry piece ($apiPath)..."
:local contentLen 0
:local content ""
:do {
/tool fetch url=$apiPath dst-path=$tempFile
:set content [/file get [/file find name=$tempFile] contents]
:set contentLen [:len $content]
} on-error={
/log info "no more pieces";
:put "no more pieces"
:set isEnd true
}
:local lineEnd 0
:local line ""
:local lastEnd 0
:local company ""
:while ($lastEnd < $contentLen) do {
:set lineEnd [:find $content "\n" $lastEnd ]
:set line [:pick $content $lastEnd $lineEnd]
:set lastEnd ($lineEnd+1)
:local entry [:pick $line 0 ($lineEnd-1)]
:if ([:pick $line 0 1] != "#") do={
:if ([:len $entry ] > 0) do={
/log info "add '$entry' subnet of '$company' to list '$listName'...";
:put "add '$entry' subnet of '$company' to list '$listName'...";
:do {
/ip firewall address-list add list=$listName address=$entry comment=$company
} on-error={
/log info "failed to add '$entry' subnet of '$company' to list '$listName', probably, it's duplication error.";
:put "failed to add '$entry' subnet of '$company' to list '$listName', probably, it's duplication error."
}
}
} else={
:set company [:pick $line 2 ($lineEnd) ]
}
}
:set i (i+1)
} while (!$isEnd)
@A-I-Demidov
Copy link

A-I-Demidov commented Sep 22, 2022

Сейчас периодически бывает недоступна закачка с uablacklist.net а скрипт при запуске первым делом удаляет те листы, что уже есть, что естественно лишает возможности обхода.

Если это возможно, будет отлично с начала проверять начало закачки, а лишь после удалять маршруты.

@A-I-Demidov
Copy link

На форуме микротика хорошее обсуждение с методами закачки листов.

Из плюсов то, что не используется диск самого роутера, что продлевает его жизнь, а так же есть методы контроля наличия достпуности url (в конце треда) и вторая методика замены адресов в виде таймаутов для листов.

https://forum.mikrotik.com/viewtopic.php?f=9&t=152632#

@A-I-Demidov
Copy link

https://forum.mikrotik.com/viewtopic.php?f=9&t=152632#

В общем сам допилил.

  1. Скрипт не задействует хранилище микротика, чем продлевает его жизнь.

  2. Работает практически моментально по сравнению с оригиналом.

  3. Скрипт теперь универсальный и кушает практически любые листы.

  4. Изменен немного принцип действия.

Теперь лист создается с таймаутом в сутки, а я советую запускать его раз в 6-12 часов для обновления таймаутов и добавления новых записей.

Сам код:

{
/ip firewall address-list
:local update do={
 :put "Starting import of address-list: $listname"
 :if ($nolog = null) do={:log warning "Starting import of address-list: $listname"}
 
 :local displayed true
 :local maxretry 3
 :local retrywaitingtime 120s
 :local retryflag true
 :for retry from=1 to=$maxretry step=1 do={
  :if (retryflag) do={ :set $retryflag false; :set $sounter 0
  :if (retry > 1) do={
   :put "Source file changed. Retring after a $retrywaitingtime wait..."
   :if ($nolog = null) do={:log warning "Source file changed. Retring after a $retrywaitingtime wait..."}
   :delay $retrywaitingtime  }
  
  :local fetchResult [/tool fetch url=$url keep-result=no as-value]
  :local filesize ($fetchResult->"total")
  :local downsize ($fetchResult->"downloaded") 
  :if ($filesize = 0 && $downsize > 0) do={ :set $filesize $downsize}

  :local start 0
  :local maxsize 64000;	        # reqeusted chunk size
  :local end ($maxsize - 1);	# because start is zero the maxsize has to be reduced by one
  :local partnumber	 ($filesize / ($maxsize / 1024)); # how many chunk are maxsize
  :local remainder	 ($filesize % ($maxsize / 1024)); # the last partly chunk 
  :if ($remainder > 0)    do={ :set $partnumber ($partnumber + 1) }; # total number of chunks
  :if ($heirule != null) do={:put "Using as extra filtering: $heirule"} else={:set $heirule "."}
 # remove the current list completely if "erase" is not present (default setting)
  :if ($noerase = null) do={  
   :if ($timeout = null) do={:set $timeout 00:00:00; :do {:foreach i in=[/ip firewall address-list find list=$listname] do={/ip firewall address-list set list=("backup".$listname) $i }} on-error={} } else={
   :do {:foreach i in=[/ip firewall address-list find list=$listname dynamic] do={/ip firewall address-list set list=("backup".$listname) $i }} on-error={} };                
   :put ("Conditional deleting all".$dynamic." entries in address-list: $listname")
   :if ($nolog = null) do={:log warning ("Conditional deleting all".$dynamic." entries in address-list: $listname")}
  } else={:put "Entries not conditional deleted in address-list: $listname"}; # ENDIF ERASE
 :for x from=1 to=$partnumber step=1 do={
   # get filesize to be compared to the orignal one and if changed then retry
   :local comparesize ([/tool fetch url=$url keep-result=no as-value]->"total")
   :if ($comparesize = 0 && $downsize > 0) do={ :set $comparesize $downsize}
   
   # fetching the chunks from the webserver when the size of the source file has not changed
   # empty array when the source file changed. No processing is done till the next complete retry
   :if ($comparesize = $filesize) do={:set $data ([:tool fetch url=$url http-header-field="Range: bytes=$start-$end" output=user as-value]->"data")} else={:set $data [:toarray ""]; :set $retryflag true}
     #:if ($ownposix = null) do={
  # determining the used delimiter in the list, when not provided in the config
   # this only run once and so the impact on the import time is low
    :local ipv4Posix	  "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}"
    :local ipv4rangePosix "^[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}\\.[0-9]{1,3}/[0-9]{1,2}"
    :local domainPosix	  "^.+\\.[a-z.]{2,7}"
    :local sdata $data;
   # removes any lines at the top of the file that could interfere with finding the correct posix. Setting remarksign is needed
    :while ([:pick $sdata 0 1] = $remarksign) do={ :set $sdata [:pick $sdata ([:find $sdata "\n"]+1) [:len $sdata]] }    
    :while ([:len $sdata]!=0 && $delimiter = null) do={ # The check on length of $sdata is for if no delimiter is found.   
       	:local sline [:pick $sdata 0 [:find $sdata "\n"]]; :local slen [:len $sline];
       	# set posix depending of type of data used in the list
       	:if ($sline ~ $ipv4Posix)	    do={:set $posix $ipv4Posix;	     :set $iden "List identified as a IPv4 list"}
       	:if ($sline ~ $ipv4rangePosix)	do={:set $posix $ipv4rangePosix; :set $iden "List identified as a IPv4 with ranges list"}
       	:if ($sline ~ $domainPosix)	    do={:set $posix $domainPosix;	 :set $iden "List identified as a domain list"}
       	:if ($sline ~ $posix) do={:put $iden}
      	:if ($sline ~ $posix) do={ # only explore the line if there is a match at the start of the line.
	      :do {:if ([:pick $sline 0 ($slen-$send)] ~ ($posix."\$") || $send > $slen) do={
	        :set $delimiter [:pick $sline ($slen-$send) ($slen-($send-1))]; :set $result true} else={:set $send ($send+1)}  
             :if ($result) do={ :set  $extra [:pick $sline ($slen-$send) ($slen-($send-1))]
              :if ( $extra = " " )   do={ :set $delimiter [:pick $sline ($slen-$send) ($slen-($send-2))] }
              :if ( $extra = "  " )  do={ :set $delimiter [:pick $sline ($slen-$send) ($slen-($send-3))] }
              :if ( $extra = "   " ) do={ :set $delimiter [:pick $sline ($slen-$send) ($slen-($send-4))] }
             }; # EndIf result
	      } while (!$result); # EndDoWhile
	    }; #IF sline posix
	:set $sdata [:pick $sdata ([:find $sdata "\n"]+1) [:len $sdata]]; # cut off the already searched lines
	:if ($delimiter != null) do={:local sdata [:toarray ""]} ; #Clearing sdata array ending the WhileDo loop
    }; #WHILE END $sdata
    :local sdata [:toarray ""]
   :if ([:len $delimiter] = 0) do={ :set $delimiter "\n"; :set $delimiterShow "New Line" } else={ :set $delimiterShow $delimiter }; # when empty use NewLine 20220529	
   #} else={:put "User defind Posix: $ownposix"; :set $posix $ownposix } ; # ENDIF ownposix = null
   :if ($delimiter != null && $displayed ) do={:set $displayed false; :put "Using config provided delimiter: \"$delimiterShow\""}
   :if ($posix = null) do={:set $posix "."}; # Use a match all posix if nothing is defined or found 
   :if (!retryflag) do={:put "Reading Part: $x $start - $end"}   
   :if ($timeout = null) do={:local timeout 00:00:00}; # if no timeout is defined make it a static entry.    
   # Only remove the first line only if you are not at the start of list
   
:while ( [:pick $data 0 1] = $remarksign) do={ :set $data [:pick $data ([:find $data "\n"]+1) [:len $data]] }; # removes the invalid line (Spamhaus) 
   
   :if ($start > 0) do={:set $data [:pick $data ([:find $data "\n"]+1) [:len $data]]}
     :while ([:len $data]!=0) do={
       :local line [:pick $data 0 [:find $data "\n"]]; # create only once and checked twice as local variable
       :if ( $line ~ $posix && $line~heirule) do={    
        :do {add list=$listname address=[:pick $data 0 [:find $data $delimiter]] comment=$comment timeout=$timeout; :set $counter ($counter + 1)} on-error={}; # on error avoids any panics        
       }; # if IP address && extra filter if present
      :set $data [:pick $data ([:find $data "\n"]+1) [:len $data]]; # removes the just added IP from the data array
      # Cut of the end of the chunks by removing the last lines...very dirty but it works
      :if ([:len $data] < 256) do={:set $data [:toarray ""]}    
     }; # while

  :set $start (($start-512) + $maxsize); # shifts the subquential start back by 512  
  :set $end (($end-512) + $maxsize); # shift the subquential ends back by 512 to keep the 
  }; # if retryflag
 }; #do for x
 
}; # for retry
 :if ($counter < 1) do={:set $resultline "Import was NOT successfull! Check if the list $listname is still being maintained."} else={:set $resultline "Completed reading $counter items into address-list $listname." } 
 :put $resultline
 :if ($nolog = null) do={:log warning $resultline }
 :if ($counter > 0) do={:do {/ip firewall address-list remove [find where list=("backup".$listname)]} on-error={} } else={
 :do {:foreach i in=[/ip firewall address-list find list=("backup".$listname)] do={/ip firewall address-list set list=$listname $i }} on-error={}
 :put "Restoring backup list: $listname" 
 :if ($nolog = null) do={:log warning "Restoring backup list: $listname"}
 }; # if counter restore on failure and remove on success
}; # do
$update url=https://uablacklist.net/subnets.txt listname=uablacklist remarksign=";" timeout=1d nolog=1
}

# To be used configline settings:
# url=	        https://name.of.the.list
# listname=	name of address-list

# Optinal settings
# timeout=	the time the entry should be active. If omited then static entries are created.
# comment=	puts this comment on every line in the choosen address-list (default: no comment)
# heirule=	this will select on a word on each line if to import or not (default: no heirule)
# noerase=	any value, then the current list is not erased (default: erase)
# ownPosix=	allow to enter a onw regEX posix to be used (not ative at this moment)
# nolog=        any value, then don't write to the log (default: writing to log)

@lbrdev
Copy link

lbrdev commented Oct 5, 2022

@A-I-Demidov проверил оба скрипта:
первый скрипт, после запуска список не обновляется:

fetching UA blacklist registry piece (https://uablacklist.net/subnets_mikrotik_0.txt)...
removing existing 'uablacklist'...
started to loading
no more pieces

второй скрипт:

Starting import of address-list: uablacklist

я подозреваю что /tool fetch выпадает с таймаутом, хотя открыть врчную адрес https://uablacklist.net/subnets.txt я могу

@A-I-Demidov
Copy link

A-I-Demidov commented Oct 5, 2022

Скрипт автора я уже не использую, хотя работать он должен.
В моей версии скрипта в логи ничего не пишется, он отрабатывает, практически моментально создавая address list в firewall.

Проверьте на всякий случай, а есть ли от вас вообще возможность его скачать, так как я видел где-то обсуждения, что сам https://uablacklist.net может блокировать провайдер и тогда надо скачивание пускать сразу через используемый вами VPN.

Как вариант могу предложить не использовать скрипт, а использовать BGP с помощью ребят с https://antifilter.network
У них основное предназначение это обход блокировок в РФ, но и наш лист они тоже подтянули, можно пользоваться или им или вообще пускать весь российский сегмент через VPN, с оглядкой что никто не балуется в сети не балуется с торрентами, а то можно забить канал.

P.S. Свой скрипт именно в этом варианте проверил, все работает.
P.P.S. Проверьте права у скрипта, все-таки он должен иметь возможность читать и записывать переменные.

@neoskynet89
Copy link

Что случилоась с uablacklist.net не работает сайт, соответственно и скрипт...

@A-I-Demidov
Copy link

Что происходит с uablacklist.net не работает сайт, соответственно и скрипт...

Сайт работает.

Возможно, у вашего провайдера он заблокирован, проверьте как разолвится его DNS имя и попробуйте перенаправить роутинг к нему сразу в VPN.

Но вообще рекомендую не список, а перейти на BGP https://antifilter.network/bgp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment